JsTree v1.0 - How to manipulate effectively the data from the backend to render the trees and operate correctly?
Posted
by
Jean Paul
on Stack Overflow
See other posts from Stack Overflow
or by Jean Paul
Published on 2012-06-13T19:08:41Z
Indexed on
2012/06/24
3:16 UTC
Read the original article
Hit count: 1279
Backend info: PHP 5 / MySQL
URL: http://github.com/downloads/vakata/jstree/jstree_pre1.0_fix_1.zip
Table structure for table
discussions_tree
--CREATE TABLE IF NOT EXISTS `discussions_tree` ( `id` int(11) NOT NULL AUTO_INCREMENT, `parent_id` int(11) NOT NULL DEFAULT '0', `user_id` int(11) NOT NULL DEFAULT '0', `label` varchar(16) DEFAULT NULL, `position` bigint(20) unsigned NOT NULL DEFAULT '0', `left` bigint(20) unsigned NOT NULL DEFAULT '0', `right` bigint(20) unsigned NOT NULL DEFAULT '0', `level` bigint(20) unsigned NOT NULL DEFAULT '0', `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, `h_label` varchar(16) NOT NULL DEFAULT '', `fulllabel` varchar(255) DEFAULT NULL, UNIQUE KEY `uidx_3` (`id`), KEY `idx_1` (`user_id`), KEY `idx_2` (`parent_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ; /*The first element should in my understanding not even be shown*/ INSERT INTO `discussions_tree` (`id`, `parent_id`, `user_id`, `label`, `position`, `left`, `right`, `level`, `type`, `h_label`, `fulllabel`) VALUES (0, 0, 0, 'Contacts', 0, 1, 1, 0, NULL, '', NULL); INSERT INTO `discussions_tree` (`id`, `parent_id`, `user_id`, `label`, `position`, `left`, `right`, `level`, `type`, `h_label`, `fulllabel`) VALUES (1, 0, 0, 'How to Tag', 1, 2, 2, 0, 'drive', '', NULL);
Front End :
I've simplified the logic, it has 6 trees actually inside of a panel and that works fine
$array = array("Discussions");
$id_arr = array("d");
$nid = 0;
foreach ($array as $k=> $value)
{
$nid++;
?>
<li id="<?=$value?>" class="label">
<a href='#<?=$value?>'><span> <?=$value?> </span></a>
<div class="sub-menu" style="height:auto; min-height:120px; background-color:#E5E5E5" >
<div class="menu" id="menu_<?=$id_arr[$k]?>" style="position:relative; margin-left:56%">
<img src="./js/jsTree/create.png" alt="" id="create" title="Create" >
<img src="./js/jsTree/rename.png" alt="" id="rename" title="Rename" >
<img src="./js/jsTree/remove.png" alt="" id="remove" title="Delete">
<img src="./js/jsTree/cut.png" alt="" id="cut" title="Cut" >
<img src="./js/jsTree/copy.png" alt="" id="copy" title="Copy">
<img src="./js/jsTree/paste.png" alt="" id="paste" title="Paste">
</div>
<div id="<?=$id_arr[$k]?>" class="jstree_container"></div>
</div>
</li>
<!-- JavaScript neccessary for this tree : <?=$value?> -->
<script type="text/javascript" >
jQuery(function ($) {
$("#<?=$id_arr[$k]?>").jstree({
// List of active plugins used
"plugins" : [ "themes", "json_data", "ui", "crrm" , "hotkeys" , "types" , "dnd", "contextmenu"],
// "ui" :{ "initially_select" : ["#node_"+ $nid ] } ,
"crrm": { "move": { "always_copy": "multitree" }, "input_width_limit":128 },
"core":{ "strings":{ "new_node" : "New Tag" }},
"themes": {"theme": "classic"},
"json_data" : {
"ajax" : {
"url" : "./js/jsTree/server-<?=$id_arr[$k]?>.php",
"data" : function (n) {
// the result is fed to the AJAX request `data` option
return {
"operation" : "get_children",
"id" : n.attr ? n.attr("id").replace("node_","") : 1,
"state" : "",
"user_id": <?=$uid?>
};
}
}
}
,
"types" : {
"max_depth" : -1,
"max_children" : -1,
"types" : {
// The default type
"default" : {
"hover_node":true,
"valid_children" : [ "default" ],
},
// The `drive` nodes
"drive" : {
// can have files and folders inside, but NOT other `drive` nodes
"valid_children" : [ "default", "folder" ],
"hover_node":true,
"icon" : {
"image" : "./js/jsTree/root.png"
},
// those prevent the functions with the same name to be used on `drive` nodes.. internally the `before` event is used
"start_drag" : false,
"move_node" : false,
"remove_node" : false
}
}
},
"contextmenu" : { "items" : customMenu , "select_node": true}
})
//Hover function binded to jstree
.bind("hover_node.jstree", function (e, data) {
$('ul li[rel="drive"], ul li[rel="default"], ul li[rel=""]').each(function(i) {
$(this).find("a").attr('href', $(this).attr("id")+".php" );
})
})
//Create function binded to jstree
.bind("create.jstree", function (e, data) {
$.post(
"./js/jsTree/server-<?=$id_arr[$k]?>.php",
{
"operation" : "create_node",
"id" : data.rslt.parent.attr("id").replace("node_",""),
"position" : data.rslt.position,
"label" : data.rslt.name,
"href" : data.rslt.obj.attr("href"),
"type" : data.rslt.obj.attr("rel"),
"user_id": <?=$uid?>
},
function (r) {
if(r.status) {
$(data.rslt.obj).attr("id", "node_" + r.id);
}
else {
$.jstree.rollback(data.rlbk);
}
}
);
})
//Remove operation
.bind("remove.jstree", function (e, data) {
data.rslt.obj.each(function () {
$.ajax({
async : false,
type: 'POST',
url: "./js/jsTree/server-<?=$id_arr[$k]?>.php",
data : {
"operation" : "remove_node",
"id" : this.id.replace("node_",""),
"user_id": <?=$uid?>
},
success : function (r) {
if(!r.status) {
data.inst.refresh();
}
}
});
});
})
//Rename operation
.bind("rename.jstree", function (e, data) {
data.rslt.obj.each(function () {
$.ajax({
async : true,
type: 'POST',
url: "./js/jsTree/server-<?=$id_arr[$k]?>.php",
data : {
"operation" : "rename_node",
"id" : this.id.replace("node_",""),
"label" : data.rslt.new_name,
"user_id": <?=$uid?>
},
success : function (r) {
if(!r.status) {
data.inst.refresh();
}
}
});
});
})
//Move operation
.bind("move_node.jstree", function (e, data) {
data.rslt.o.each(function (i) {
$.ajax({
async : false,
type: 'POST',
url: "./js/jsTree/server-<?=$id_arr[$k]?>.php",
data : {
"operation" : "move_node",
"id" : $(this).attr("id").replace("node_",""),
"ref" : data.rslt.cr === -1 ? 1 : data.rslt.np.attr("id").replace("node_",""),
"position" : data.rslt.cp + i,
"label" : data.rslt.name,
"copy" : data.rslt.cy ? 1 : 0,
"user_id": <?=$uid?>
},
success : function (r) {
if(!r.status) {
$.jstree.rollback(data.rlbk);
}
else {
$(data.rslt.oc).attr("id", "node_" + r.id);
if(data.rslt.cy && $(data.rslt.oc).children("UL").length) {
data.inst.refresh(data.inst._get_parent(data.rslt.oc));
}
}
}
});
});
});
// This is for the context menu to bind with operations on the right clicked node
function customMenu(node) {
// The default set of all items
var control;
var items = {
createItem: {
label: "Create",
action: function (node) { return {createItem: this.create(node) }; }
},
renameItem: {
label: "Rename",
action: function (node) { return {renameItem: this.rename(node) }; }
},
deleteItem: {
label: "Delete",
action: function (node) { return {deleteItem: this.remove(node) }; },
"separator_after": true
},
copyItem: {
label: "Copy",
action: function (node) { $(node).addClass("copy"); return {copyItem: this.copy(node) }; }
},
cutItem: {
label: "Cut",
action: function (node) { $(node).addClass("cut"); return {cutItem: this.cut(node) }; }
},
pasteItem: {
label: "Paste",
action: function (node) { $(node).addClass("paste"); return {pasteItem: this.paste(node) }; }
}
};
// We go over all the selected items as the context menu only takes action on the one that is right clicked
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element)
{
if ( $(element).attr("id") != $(node).attr("id") )
{
// Let's deselect all nodes that are unrelated to the context menu -- selected but are not the one right clicked
$("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
}
});
//if any previous click has the class for copy or cut
$("#<?=$id_arr[$k]?>").find("li").each(function(index,element)
{
if ($(element) != $(node) )
{
if( $(element).hasClass("copy") || $(element).hasClass("cut") ) control=1;
}
else if( $(node).hasClass("cut") || $(node).hasClass("copy"))
{
control=0;
}
});
//only remove the class for cut or copy if the current operation is to paste
if($(node).hasClass("paste") )
{
control=0;
// Let's loop through all elements and try to find if the paste operation was done already
$("#<?=$id_arr[$k]?>").find("li").each(function(index,element)
{
if( $(element).hasClass("copy") ) $(this).removeClass("copy");
if ( $(element).hasClass("cut") ) $(this).removeClass("cut");
if ( $(element).hasClass("paste") ) $(this).removeClass("paste");
});
}
switch (control)
{
//Remove the paste item from the context menu
case 0:
switch ($(node).attr("rel"))
{
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
delete items.pasteItem;
break;
case "default":
delete items.pasteItem;
break;
}
break;
//Remove the paste item from the context menu only on the node that has either copy or cut added class
case 1:
if( $(node).hasClass("cut") || $(node).hasClass("copy") )
{
switch ($(node).attr("rel"))
{
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
delete items.pasteItem;
break;
case "default":
delete items.pasteItem;
break;
}
}
else //Re-enable it on the clicked node that does not have the cut or copy class
{
switch ($(node).attr("rel"))
{
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
break;
}
}
break;
//initial state don't show the paste option on any node
default: switch ($(node).attr("rel"))
{
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
delete items.pasteItem;
break;
case "default":
delete items.pasteItem;
break;
}
break;
}
return items;
}
$("#menu_<?=$id_arr[$k]?> img").hover(
function () {
$(this).css({'cursor':'pointer','outline':'1px double teal'})
},
function () {
$(this).css({'cursor':'none','outline':'1px groove transparent'})
}
);
$("#menu_<?=$id_arr[$k]?> img").click(function () {
switch(this.id) {
//Create only the first element
case "create":
if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
{
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){
switch(index)
{
case 0: $("#<?=$id_arr[$k]?>").jstree("create", '#'+$(element).attr("id"), null, /*{attr : {href: '#' }}*/null ,null, false);
break;
default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
break;
}
});
}
else
{
$.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
setTimeout(function(){
$.facebox.close();
}, 2000);
}
break;
//REMOVE
case "remove":
if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
{
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){
//only execute if the current node is not the first one (drive)
if( $(element).attr("id") != $("div.jstree > ul > li").first().attr("id") )
{
$("#<?=$id_arr[$k]?>").jstree("remove",'#'+$(element).attr("id"));
}
else $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
});
}
else
{
$.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
setTimeout(function(){
$.facebox.close();
}, 2000);
}
break;
//RENAME NODE only one selection
case "rename":
if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
{
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){
if( $(element).attr("id") != $("div.jstree > ul > li").first().attr("id") )
{
switch(index)
{
case 0: $("#<?=$id_arr[$k]?>").jstree("rename", '#'+$(element).attr("id") );
break;
default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
break;
}
}
else $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
});
}
else
{
$.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
setTimeout(function(){
$.facebox.close();
}, 2000);
}
break;
//Cut
case "cut":
if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
{
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){
switch(index)
{
case 0:
$("#<?=$id_arr[$k]?>").jstree("cut", '#'+$(element).attr("id"));
$.facebox('<p class=\'p_inner teal\'>Operation "Cut" successfully done.<p class=\'p_inner teal bold\'>Where to place it?');
setTimeout(function(){
$.facebox.close();
$("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id"));
}, 2000);
break;
default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
break;
}
});
}
else
{
$.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
setTimeout(function(){
$.facebox.close();
}, 2000);
}
break;
//Copy
case "copy":
if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
{
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){
switch(index)
{
case 0:
$("#<?=$id_arr[$k]?>").jstree("copy", '#'+$(element).attr("id"));
$.facebox('<p class=\'p_inner teal\'>Operation "Copy": Successfully done.<p class=\'p_inner teal bold\'>Where to place it?');
setTimeout(function(){
$.facebox.close();
$("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
}, 2000);
break;
default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
break;
}
});
}
else
{
$.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
setTimeout(function(){
$.facebox.close();
}, 2000);
}
break;
case "paste":
if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
{
$.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){
switch(index)
{
case 0:
$("#<?=$id_arr[$k]?>").jstree("paste", '#'+$(element).attr("id"));
break;
default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
break;
}
});
}
else
{
$.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
setTimeout(function(){
$.facebox.close();
}, 2000);
}
break;
}
});
<?
}
?>
server.php
$path='../../../..';
require_once "$path/phpfoo/dbif.class";
require_once "$path/global.inc";
// Database config & class
$db_config = array(
"servername"=> $dbHost,
"username" => $dbUser,
"password" => $dbPW,
"database" => $dbName
);
if(extension_loaded("mysqli")) require_once("_inc/class._database_i.php");
else require_once("_inc/class._database.php");
//Tree class
require_once("_inc/class.ctree.php");
$dbLink = new dbif();
$dbErr = $dbLink->connect($dbName,$dbUser,$dbPW,$dbHost);
$jstree = new json_tree();
if(isset($_GET["reconstruct"])) {
$jstree->_reconstruct();
die();
}
if(isset($_GET["analyze"])) {
echo $jstree->_analyze();
die();
}
$table = '`discussions_tree`';
if($_REQUEST["operation"] && strpos($_REQUEST["operation"], "_") !== 0 && method_exists($jstree, $_REQUEST["operation"])) {
foreach($_REQUEST as $k => $v)
{
switch($k)
{
case 'user_id':
//We are passing the user_id from the $_SESSION on each request and trying to pick up the min and max value from the table that matches the 'user_id'
$sql = "SELECT max(`right`) , min(`left`) FROM $table WHERE `user_id`=$v";
//If the select does not return any value then just let it be :P
if (!list($right, $left)=$dbLink->getRow($sql))
{
$sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v WHERE `id` = 1 AND `parent_id` = 0");
$sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v WHERE `parent_id` = 1 AND `label`='How to Tag' ");
}
else
{
$sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v, `right`=$right+2 WHERE `id` = 1 AND `parent_id` = 0");
$sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v, `left`=$left+1, `right`=$right+1 WHERE `parent_id` = 1 AND `label`='How to Tag' ");
}
break;
}
}
header("HTTP/1.0 200 OK");
header('Content-type: application/json; charset=utf-8');
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Pragma: no-cache");
echo $jstree->{$_REQUEST["operation"]}($_REQUEST);
die();
}
header("HTTP/1.0 404 Not Found");
?>
The problem: DND *(Drag and Drop) works, Delete works, Create works, Rename works, but Copy, Cut and Paste don't work
© Stack Overflow or respective owner