Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply drag and drop/attach feature to JSTreegraph using Jquery draggable

I am using JSTreegraph plugin to draw a tree structure. But now I need a drag, drop and attach feature wherein I can drag any node of the tree and attach to any other node and subsequently all the children of the first node will be now the grand-children of the new node (to which it is attached).

As far as I know this plugin doesnt seem to have this feature. It simply draws the structure based on the data object passed to it.

The plugin basically assigns a class Node to all the nodes(divs) of the tree and another class NodeHover to a node on hover. No id is assigned to these divs.

So I tried using JQuery Draggable just to see if any of the node can be moved by doing this

$('.Node').draggable();
$('.NodeHover').draggable();

But it doesnt seem to work. So can anyone help me with this.

How can I get drag and attach functionality?

*EDIT:: Pardon me am not so great with using Fiddle, so am sharing a sample code here for your use:*

HTML file: which will draw the sample tree

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <link href="css/JSTreeGraph.css" rel="stylesheet" type="text/css" />
  <script src="js/JSTreeGraph.js" type="text/javascript"></script>
  <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>
  <script src="js/jquery.ui.position.js" type="text/javascript"></script>
  <style type="text/css">
     .Container {
       position: absolute;
      top: 100px;
      left: 50px;
       id: Container; 
    }


 </style>
</head>
<body>
    <div id="tree">
        Ctrl+Click to Add Node
        <br />
        Double Click to Expand or Collapse
        <br />
        Shift+Click to Delete Node
        <br />
        <select id="dlLayout" onchange="ChangeLayout()">
            <option value="Horizontal">
                Horizontal
            </option>
            <option value="Vertical" selected>
                Vertical
            </option>
        </select>
        <div class="Container" id="dvTreeContainer"></div>


        <script type="text/javascript">

    var selectedNode;
    // Root node
    var rootNode = { Content: "Onida", Nodes:[] };

    // First Level
    rootNode.Nodes[0] = { Content: "Employee Code", navigationType: "0"};
    rootNode.Nodes[1] = { Content: "Problem Area", navigationType: "1"  };

    // Second Level
    rootNode.Nodes[1].Nodes = [{ Content : "ACC-HO", Collapsed: true /* This node renders collapsed */ }, 
                               { Content : "ACC-SALES" },
                               { Content : "BUSI. HEAD",   /*This node looks different*/ ToolTip: "Click ME!" },
                               { Content : "CEO"},
                               { Content : "HO-ADMIN"},
                               { Content : "HO-FACTORY"},
                               { Content : "SALES"}];

    // Third Level
    rootNode.Nodes[1].Nodes[0].Nodes = [{ Content: "Billing" },
                                        { Content: "Credit Limit" },
                                        { Content: "Reconciliation" }];

    rootNode.Nodes[1].Nodes[1].Nodes = [{ Content: "Billing" },
                                        { Content: "Others" }];

    rootNode.Nodes[1].Nodes[2].Nodes = [{ Content: "AC" },
                                        { Content: "CTV" },
                                        { Content: "DVD" },
                                        { Content: "Washing Machine" }];

    rootNode.Nodes[1].Nodes[6].Nodes = [{ Content: "Appointments" },
                                        { Content: "Resignations" },
                                        { Content: "Others" }];

    // Draw the tree for the first time
    RefreshTree();


    function RefreshTree() {
        DrawTree({ Container: document.getElementById("dvTreeContainer"),
                    RootNode: rootNode,
                    Layout: document.getElementById("dlLayout").value,
                    OnNodeClickFirefox: NodeClickFF,
                    OnNodeClickIE: NodeClickIE,
                    OnNodeDoubleClick: NodeDoubleClick
                    });
    }

    //function 
    function NodeClickFF(e) {
        if (e.shiftKey){
            // Delete Node
            if (!this.Node.Collapsed) {
                for(var index=0; index<this.Node.ParentNode.Nodes.length; index++) {
                    if(this.Node.ParentNode.Nodes[index].Content == this.Node.Content) {
                        this.Node.ParentNode.Nodes.splice(index, 1);
                        break;
                    }
                }
               RefreshTree();
            }
                // return false;
        }
        else if (e.ctrlKey) {
            // Add new Child if Expanded                    
            if (!this.Node.Collapsed) {
                if (!this.Node.Nodes) this.Node.Nodes = new Array();
                var newNodeIndex = this.Node.Nodes.length;
                this.Node.Nodes[newNodeIndex] = new Object();
                this.Node.Nodes[newNodeIndex].Content = this.Node.Content + "." + (newNodeIndex + 1);
                RefreshTree();
            }
                // return false;
        }
        else{
            fnNodeProperties(this.Node);
        }
    }

    function NodeClickIE() {
        if (typeof(event) == "undefined" && event.ctrlKey) {
            // Add new Child if Expanded
            if (!this.Node.Collapsed) {
                if (!this.Node.Nodes) this.Node.Nodes = new Array();
                var newNodeIndex = this.Node.Nodes.length;
                this.Node.Nodes[newNodeIndex] = new Object();
                this.Node.Nodes[newNodeIndex].Content = this.Node.Content + "." + (newNodeIndex + 1);
                RefreshTree();
            }
        }
        else if (typeof(event) == "undefined" && event.shiftKey) {
            // Delete Node
            if (!this.Node.Collapsed) {
                for(var index=0; index<this.Node.ParentNode.Nodes.length; index++) {
                    if(this.Node.ParentNode.Nodes[index].Content == this.Node.Content) {
                        this.Node.ParentNode.Nodes.splice(index, 1);
                        break;
                    }
                }
                RefreshTree();
            }
        }
        else{
            fnNodeProperties(this.Node);
        }
    }        

    function NodeDoubleClick() {
        if (this.Node.Nodes && this.Node.Nodes.length > 0) { // If has children
            this.Node.Collapsed = !this.Node.Collapsed;
            RefreshTree();
        }
    }

    function ChangeLayout() {

        RefreshTree();
    }

</script>
    </div>
</body>
</html>

JSTreeGraph JS file: plugin js file

function DrawTree(options) {

// Prepare Nodes
PrepareNode(options.RootNode);

// Calculate Boxes Positions
if (options.Layout == "Vertical") {
    PerformLayoutV(options.RootNode);
} else {
    PerformLayoutH(options.RootNode);
}

// Draw Boxes
options.Container.innerHTML = "";
DrawNode(options.RootNode, options.Container, options);

// Draw Lines
DrawLines(options.RootNode, options.Container);
}

 function DrawLines(node, container) {
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has children and Is Expanded
    for (var j = 0; j < node.Nodes.length; j++) {
        if(node.ChildrenConnectorPoint.Layout=="Vertical")
            DrawLineV(container, node.ChildrenConnectorPoint, node.Nodes[j].ParentConnectorPoint);
        else
            DrawLineH(container, node.ChildrenConnectorPoint, node.Nodes[j].ParentConnectorPoint);

        // Children
        DrawLines(node.Nodes[j], container);
    }
}
}

 function DrawLineH(container, startPoint, endPoint) {
    var midY = (startPoint.Y + ((endPoint.Y - startPoint.Y) / 2)); // Half path between start en end Y point

    // Start segment
    DrawLineSegment(container, startPoint.X, startPoint.Y, startPoint.X, midY, 1);

    // Intermidiate segment
    var imsStartX = startPoint.X < endPoint.X ? startPoint.X : endPoint.X; // The lower value will be the starting point
    var imsEndX = startPoint.X > endPoint.X ? startPoint.X : endPoint.X; // The higher value will be the ending point
    DrawLineSegment(container, imsStartX, midY, imsEndX, midY, 1);

    // End segment
    DrawLineSegment(container, endPoint.X, midY, endPoint.X, endPoint.Y, 1);
 }

  function DrawLineV(container, startPoint, endPoint) {
var midX = (startPoint.X + ((endPoint.X - startPoint.X) / 2)); // Half path between start en end X point

// Start segment
DrawLineSegment(container, startPoint.X, startPoint.Y, midX, startPoint.Y, 1);

// Intermidiate segment
var imsStartY = startPoint.Y < endPoint.Y ? startPoint.Y : endPoint.Y; // The lower value will be the starting point
var imsEndY = startPoint.Y > endPoint.Y ? startPoint.Y : endPoint.Y; // The higher value will be the ending point
DrawLineSegment(container, midX, imsStartY, midX, imsEndY, 1);

// End segment
DrawLineSegment(container, midX, endPoint.Y, endPoint.X, endPoint.Y, 1);
}

function DrawLineSegment(container, startX, startY, endX, endY, lineWidth) {

var lineDiv = document.createElement("div");
lineDiv.style.top = startY + "px";
lineDiv.style.left = startX + "px";

if (startX == endX) { // Vertical Line
    lineDiv.style.width = lineWidth + "px";
    lineDiv.style.height = (endY - startY) + "px";
}
else{ // Horizontal Line
    lineDiv.style.width = (endX - startX) + "px";
    lineDiv.style.height = lineWidth + "px";
}

lineDiv.className = "NodeLine";
container.appendChild(lineDiv);
}

function DrawNode(node, container, options) {
var nodeDiv = document.createElement("div");
nodeDiv.style.top  = node.Top + "px";
nodeDiv.style.left = node.Left + "px";
nodeDiv.style.width = node.Width + "px";
nodeDiv.style.height = node.Height + "px";

if (node.Collapsed) {
    nodeDiv.className = "NodeCollapsed";
} else {
    nodeDiv.className = "Node";
}

if (node.Class) 
    nodeDiv.className = node.Class;

if (node.Content)
    nodeDiv.innerHTML = "<div class='NodeContent'>" + node.Content + "</div>";

if (node.ToolTip)
    nodeDiv.setAttribute("title", node.ToolTip);

nodeDiv.Node = node;

// Events
if (options.OnNodeClickIE){
    //alert('OnNodeClick');
    nodeDiv.onclick = options.OnNodeClickIE;        
}
// Events
if (options.OnNodeClickFirefox){
    //alert('OnNodeClick');
    nodeDiv.onmousedown = options.OnNodeClickFirefox;        
}
//on right click
if (options.OnContextMenu){
        //alert('OnContextMenu');
        nodeDiv.oncontextmenu = options.OnContextMenu;
     }

if (options.OnNodeDoubleClick)
    nodeDiv.ondblclick = options.OnNodeDoubleClick;

nodeDiv.onmouseover = function () { // In
    this.PrevClassName = this.className;
    this.className = "NodeHover";
};

nodeDiv.onmouseout = function () { // Out
    if (this.PrevClassName) {
        this.className = this.PrevClassName;
        this.PrevClassName = null;
    }
};

container.appendChild(nodeDiv);

// Draw children
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has Children and is Expanded
    for (var i = 0; i < node.Nodes.length; i++) {
        DrawNode(node.Nodes[i], container, options);
    }
}
}

function PerformLayoutV(node) {

var nodeHeight = 30;
var nodeWidth = 100;
var nodeMarginLeft = 40;
var nodeMarginTop = 20;

var nodeTop = 0; // defaultValue 

// Before Layout this Node, Layout its children
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) {
    for (var i = 0; i < node.Nodes.length; i++) {
        PerformLayoutV(node.Nodes[i]);
    }
}

if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // If Has Children and Is Expanded

    // My Top is in the center of my children
    var childrenHeight = (node.Nodes[node.Nodes.length - 1].Top + node.Nodes[node.Nodes.length - 1].Height) - node.Nodes[0].Top;
    nodeTop = (node.Nodes[0].Top + (childrenHeight / 2)) - (nodeHeight / 2);

    // Is my top over my previous sibling?
    // Move it to the bottom
    if (node.LeftNode && ((node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop) > nodeTop)) {
        var newTop = node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop;
        var diff = newTop - nodeTop;
        /// Move also my children
        MoveBottom(node.Nodes, diff);
        nodeTop = newTop;
    }

} else {
    // My top is next to my top sibling
    if (node.LeftNode)
        nodeTop = node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop;
}

node.Top = nodeTop;

// The Left depends only on the level
node.Left = (nodeMarginLeft * (node.Level + 1)) + (nodeWidth * (node.Level + 1));
// Size is constant
node.Height = nodeHeight;
node.Width = nodeWidth;

// Calculate Connector Points
// Child: Where the lines get out from to connect this node with its children
var pointX = node.Left + nodeWidth;
var pointY = nodeTop + (nodeHeight/2);
node.ChildrenConnectorPoint = { X: pointX, Y: pointY, Layout: "Vertical" };
// Parent: Where the line that connect this node with its parent end
pointX = node.Left;
pointY = nodeTop + (nodeHeight/2);
node.ParentConnectorPoint = { X: pointX, Y: pointY, Layout: "Vertical" };
}

function PerformLayoutH(node) {

var nodeHeight = 30;
var nodeWidth = 100;
var nodeMarginLeft = 20;
var nodeMarginTop = 50;

var nodeLeft = 0; // defaultValue 

// Before Layout this Node, Layout its children
if ((!node.Collapsed) && node.Nodes && node.Nodes.length>0) {
    for (var i = 0; i < node.Nodes.length; i++) {
        PerformLayoutH(node.Nodes[i]);
    }
}

if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // If Has Children and Is Expanded

    // My left is in the center of my children
    var childrenWidth = (node.Nodes[node.Nodes.length-1].Left + node.Nodes[node.Nodes.length-1].Width) - node.Nodes[0].Left;
    nodeLeft = (node.Nodes[0].Left + (childrenWidth / 2)) - (nodeWidth / 2);

    // Is my left over my left node?
    // Move it to the right
    if(node.LeftNode&&((node.LeftNode.Left+node.LeftNode.Width+nodeMarginLeft)>nodeLeft)) {
        var newLeft = node.LeftNode.Left + node.LeftNode.Width + nodeMarginLeft;
        var diff = newLeft - nodeLeft;
        /// Move also my children
        MoveRigth(node.Nodes, diff);
        nodeLeft = newLeft;
    }
} else {
    // My left is next to my left sibling
    if (node.LeftNode) 
        nodeLeft = node.LeftNode.Left + node.LeftNode.Width + nodeMarginLeft;
}

node.Left = nodeLeft;

// The top depends only on the level
node.Top = (nodeMarginTop * (node.Level + 1)) + (nodeHeight * (node.Level + 1));
// Size is constant
node.Height = nodeHeight;
node.Width = nodeWidth;

// Calculate Connector Points
// Child: Where the lines get out from to connect this node with its children
var pointX = nodeLeft + (nodeWidth / 2);
var pointY = node.Top + nodeHeight;
node.ChildrenConnectorPoint = { X: pointX, Y: pointY, Layout:"Horizontal" };
// Parent: Where the line that connect this node with its parent end
pointX = nodeLeft + (nodeWidth / 2);
pointY = node.Top;
node.ParentConnectorPoint = { X: pointX, Y: pointY, Layout: "Horizontal" };
}

function MoveRigth(nodes, distance) {
for (var i = 0; i < nodes.length; i++) {
    nodes[i].Left += distance;
    if (nodes[i].ParentConnectorPoint) nodes[i].ParentConnectorPoint.X += distance;
    if (nodes[i].ChildrenConnectorPoint) nodes[i].ChildrenConnectorPoint.X += distance;
    if (nodes[i].Nodes) {
        MoveRigth(nodes[i].Nodes, distance);
    }
}
}

function MoveBottom(nodes, distance) {
for (var i = 0; i < nodes.length; i++) {
    nodes[i].Top += distance;
    if (nodes[i].ParentConnectorPoint) nodes[i].ParentConnectorPoint.Y += distance;
    if (nodes[i].ChildrenConnectorPoint) nodes[i].ChildrenConnectorPoint.Y += distance;
    if (nodes[i].Nodes) {
        MoveBottom(nodes[i].Nodes, distance);
    }   
}
}

function PrepareNode(node, level, parentNode, leftNode, rightLimits) {

if (level == undefined) level = 0;
if (parentNode == undefined) parentNode = null;
if (leftNode == undefined) leftNode = null;
if (rightLimits == undefined) rightLimits = new Array();

node.Level = level;
node.ParentNode = parentNode;
node.LeftNode = leftNode;

if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has children and is expanded
    for (var i = 0; i < node.Nodes.length; i++) {
        var left = null;
        if (i == 0 && rightLimits[level]!=undefined) left = rightLimits[level];
        if (i > 0) left = node.Nodes[i - 1];
        if (i == (node.Nodes.length-1)) rightLimits[level] = node.Nodes[i];
        PrepareNode(node.Nodes[i], level + 1, node, left, rightLimits);
    }
}
}

JSTreeGraph CSS file:

.NodeContent
{
font-family:Verdana;
font-size:small;
}

.Node 
{
position:absolute;
background-color: #CCDAFF;
border: 1px solid #5280FF;
text-align:center;
vertical-align:middle;
cursor:pointer;
overflow:hidden;
}

.NodeHover
{
position:absolute;
background-color: #8FADFF;
border: 1px solid #5280FF;
text-align:center;
vertical-align:middle;
cursor:pointer;
overflow:hidden;
}

.NodeCollapsed
{
position:absolute;
background-color: #8FADFF;
border: 2px solid black;
text-align:center;
vertical-align:middle;
cursor:pointer;
overflow:hidden;
}


.NodeLine
{
background-color: #000066;
position:absolute;
overflow:hidden;
}
like image 514
DarkKnightFan Avatar asked Dec 18 '12 05:12

DarkKnightFan


1 Answers

I supose that what you need is to stablish a mechanism to modify your tree logic structure on dragging and then reload the whole tree element. This way the plugin will render your new modified structure.

like image 86
Bardo Avatar answered Oct 16 '22 23:10

Bardo