function JsTree()
{
	this.nodes = {};
	this.var_name = '';
	this.container_prefix = '';
	this.pm_path = '/d/i/tree/';
	this.open_nodes = [];
	this.data_loading = false;
	this.urls = {};
}

JsTree.prototype.render = function(node_id)
{
	this.data_loading = true;

	new Ajax.Request(this.urls.load_data + node_id, {
		method: 'GET',
		asynchronous: true,
		onSuccess: new Function('r', this.var_name + '.receiveData(r)'),
		parameters: {
			rand: Math.random()
		}
	});
}

JsTree.prototype.receiveData = function(r)
{
	try
	{
		eval(r.responseText);
	}
	catch (e)
	{
		return;
	}

	for (var n = 0; n < d.length; n++)
	{
		d[n].container_prefix = this.container_prefix;
		this.nodes[d[n].id] = d[n];
	}

	if (node_id > 0)
	{
		this.nodes[node_id].children = d;
	}

	var container = $(node_id == 0 ? this.container_prefix : this.nodes[node_id].getIdOfCC());
	this.renderChildren(container, d);
	this.data_loading = false;
}

JsTree.prototype.renderChildren = function(container, children, level)
{
	if (isNaN(level = parseInt(level)))
		level = 0;

	this.removeProgressMessage(container);

	for (var n = 0; n < children.length; n++)
	{
		var c = children[n],
			tr = container.insertRow(-1);

		this.renderNode(c, tr, container);
		this.renderChildContainer(c, tr, container);
	}
}

JsTree.prototype.renderNode = function(node, container, top_container)
{
	this.renderPm(node, container, top_container);

	var cc = container.insertCell(-1);

	this.renderCaption(node, cc, top_container);
	this.renderTools(node, cc, top_container);
	this.renderAdmins(node, cc, top_container);
}

JsTree.prototype.renderAdmins = function(node, container, top_container)
{
	var tr_a = $(node.getIdOfAc());

	if (tr_a == null)
	{
		tr_a = top_container.insertRow(-1);
		tr_a.id = node.getIdOfAc();
		tr_a.style.display = 'none';
	}

	var tr_c = tr_a.insertCell(-1);
	tr_c = tr_a.insertCell(-1);
	tr_c.className = 'tree-admins';

	var a_hdr = document.createElement('div');
	a_hdr.innerHTML = 'Назначены администраторы';
	tr_c.appendChild(a_hdr);

	var noadmins = true;
	var tool = new JsTreeTools();
	for (var admin_id in node.admins)
	{
		if (this.potential_admins[admin_id] == null)
			continue;

		var a_dv = document.createElement('div'),
			admin_fio = this.potential_admins[admin_id],
			fio_spn = document.createElement('span');

		fio_spn.className = 'tree-admins-fio';
		fio_spn.innerHTML = admin_fio;
		a_dv.appendChild(fio_spn);

		a_dv.appendChild(tool.create({
			img: 'dels',
			url: 'javascript:' + this.var_name + '.removeAdmin(' + node.id + ', ' + admin_id + ')',
			title: 'лишить прав',
			confirm: 'Удалить пользователя ' + admin_fio + ' из списка администраторов вершины ' + node.name + '?'
		}));

		noadmins = false;

		tr_c.appendChild(a_dv);
	}

	if (noadmins)
	{
		var d_na = document.createElement('div');
		d_na.className = 'tree-admins-fio';
		d_na.innerHTML = 'нет';
		tr_c.appendChild(d_na);
	}

	var sl = document.createElement('select');
	sl.id = node.getIdOfAdmSelect();

	for (var user_id in this.potential_admins)
	{
		if (node.admins[user_id] != null)
			continue;

		var option = document.createElement('option');
		option.value = user_id;
		option.text = this.potential_admins[user_id];
		sl.options.add(option, -1);
	}

	var d_add = document.createElement('div'),
		d_adds = document.createElement('span');

	d_adds.innerHTML = 'добавить';
	d_adds.className = 'tree-admins-add';
	d_add.appendChild(d_adds);

	d_add.appendChild(sl);
	d_add.appendChild(tool.create({
		img: 'add',
		url: 'javascript:' + this.var_name + '.addAdmin(' + node.id + ')',
		title: 'назначить'
	}));

	tr_c.appendChild(d_add);
}

JsTree.prototype.renderPm = function(node, container, top_container)
{
	var pmc = container.insertCell(-1),
		pm = document.createElement('img');

	pmc.className = 'tree-pm';

	if (node.has_children > 0)
	{
		pm.id = node.getIdOfPm();//this.getNodePartId(node.id, '-pm');
		pm.src = this.pm_path + 'plus.gif';
		pm.style.cursor = 'pointer';
		pm.onclick = new Function('', this.var_name + '.ec(' + node.id + ')');
	}
	else
	{
		pm.src = this.pm_path + 'null.gif';
	}

	pmc.appendChild(pm);
}

JsTree.prototype.renderCaption = function(node, container, top_container)
{
	var cc = document.createElement('span');
	cc.className = 'tree-caption';
	cc.innerHTML = node.name;
	container.appendChild(cc);
}

JsTree.prototype.renderTools = function(node, container, top_container)
{
	var c_tl = document.createElement('span'),
		tools = this.getTools(node);

	c_tl.className = 'tree-tools';

	for (var n = 0; n < tools.length; n++)
		c_tl.appendChild(tools[n]);

	container.appendChild(c_tl);
}

JsTree.prototype.renderChildContainer = function(node, container, top_container)
{
	var c_row = top_container.insertRow(-1),
		c_cell;

	for (var n = 0; n < 2; n++)
		c_cell = c_row.insertCell(-1);

	c_cell.colSpan = 2;
	c_cell.style.margin = '0px';
	c_cell.style.padding = '0px';

	var c_tbl = document.createElement('table');
	c_tbl.cellSpacing = 1;
	c_tbl.cellPadding = 2;
	c_tbl.className = 'tree-child-area';
	c_tbl.style.display = 'none';
	c_tbl.id = node.getIdOfCC();

	c_cell.appendChild(c_tbl);
}

JsTree.prototype.ec = function(node_id)
{
	var node = this.nodes[node_id];
	if (node == null)
		return;

	var cc = $(node.getIdOfCC()),
		need_open = cc.style.display == 'none';

	if (need_open && node.onscreen == 0)
	{
		this.loadNodeData(node);
	}

	cc.style.display = need_open ? 'block' : 'none';
	if ($(node.getIdOfPm()))
	{
		$(node.getIdOfPm()).src = this.pm_path + (need_open ? 'minus' : 'plus') + '.gif';
	}

	this.updateSavedTree(node, need_open);
}

JsTree.prototype.loadNodeData = function(node)
{
	if (typeof(node.children) != 'object')
	{
		var cc = $(node.getIdOfCC());

		if (cc.rows.length == 0)
		{
			var tr = cc.insertRow(-1),
				td = tr.insertCell(-1),
				img = document.createElement('img');

			img.src = '/d/i/tree/l.gif'
			td.appendChild(img);

			td = tr.insertCell(-1);
			td.className = 'tree-data-loading';
			td.innerHTML = 'Идет загрузка ...';

			this.render(node.id);
		}
	}
}

JsTree.prototype.removeProgressMessage = function(table)
{
	while (table.rows.length > 0)
		table.deleteRow(0);
}

JsTree.prototype.updateSavedTree = function(node, need_open)
{
	if (this.urls.update_tree)
	{
		new Ajax.Request(this.urls.update_tree + node.id, {
			method: 'GET',
			asynchronous: true,
			parameters: {
				show: need_open ? 1 : 0,
				rand: Math.random()
			}
		});
	}
}

JsTree.prototype.expandNodes = function()
{
	while (this.open_nodes.length > 0)
	{
		if (this.data_loading)
		{
			window.setTimeout(this.var_name + '.expandNodes()', 200);
			return;
		}

		this.ec(this.open_nodes.shift());
	}
}

JsTree.prototype.getTools = function(node)
{
	var tools = new JsTreeTools();

	this.addTools(tools, node);

	return tools.tools;
}

JsTree.prototype.addTools = function(tools, node)
{
	if (node.tools[1] != null)
	{
		tools.add({
			img: 'add',
			url: this.urls.tool_base + 'edit/?parent_id=' + node.id,
			title: 'добавить'
		});
	}

	if (node.tools[2] != null)
	{
		tools.add({
			img: 'order',
			url: this.urls.tool_base + 'order/' + node.id,
			title: 'порядок'
		});
	}

	if (node.tools[3] != null)
	{
		tools.add({
			img: 'edit',
			url: this.urls.tool_base + 'edit/' + node.id,
			title: 'редактировать'
		});
	}

	if (node.tools[4] != null)
	{
		tools.add({
			img: 'del',
			url: this.urls.tool_base + 'drop/' + node.id,
			title: 'удалить',
			confirm: this.getDeleteConfirmation(node)
		});
	}

	if (node.tools[5] != null)
	{
		tools.add({
			img: 'admin',
			url: 'javascript:' + this.var_name + '.toggleAdmins(' + node.id + ')',
			title: 'администраторы'
		});
	}
}

JsTree.prototype.getDeleteConfirmation = function(node)
{
	return 'Вы уверены ?';
}

JsTree.prototype.toggleAdmins = function(node_id)
{
	var node = this.nodes[node_id];
	if (node == null)
		return;

	$(node.getIdOfAc()).toggle();
}

JsTree.prototype.addAdmin = function(node_id)
{
	var node = this.nodes[node_id];
	if (node == null)
		return;

	var admin_id = $(node.getIdOfAdmSelect()).value;
	if (!confirm('Назначить пользователя ' + this.potential_admins[admin_id] + ' администратором вершины ' + node.name + '?'))
		return;

	new Ajax.Request(this.urls.add_admin + node.id, {
		method: 'GET',
		asynchronous: true,
		parameters: {
			user_id: admin_id,
			rand: Math.random()
		},
		onSuccess: new Function('', this.var_name + '.addAdminEntry(' + node_id + ', ' + admin_id + ')'),
		onFailure: new Function('', 'alert("не удалось назначить администратора");')
	});
}

JsTree.prototype.addAdminEntry = function(node_id, admin_id)
{
	var node = this.nodes[node_id];
	if (node == null)
		return;

	if (this.potential_admins[admin_id] == null)
		return;

	node.admins[admin_id] = 1;
	this.redrawAdminArea(node);
}

JsTree.prototype.removeAdminEntry = function(node_id, admin_id)
{
	var node = this.nodes[node_id];
	if (node == null)
		return;

	if (this.potential_admins[admin_id] == null)
		return;

	var new_admins = {};
	for (var ex_admin_id in node.admins)
		if (ex_admin_id != admin_id)
			new_admins[ex_admin_id] = node.admins[ex_admin_id];

	node.admins = new_admins;
	this.redrawAdminArea(node);
}

JsTree.prototype.redrawAdminArea = function(node)
{
	var tr = $(node.getIdOfAc());
	if (tr == null)
		return;

	while (tr.cells.length > 0)
		tr.deleteCell(0);

	var table = tr.parentNode.parentNode;
	this.renderAdmins(node, null, table);
}

JsTree.prototype.removeAdmin = function(node_id, admin_id)
{
	if (this.nodes[node_id] == null)
		return;

	if (this.potential_admins[admin_id] == null)
		return;

	new Ajax.Request(this.urls.remove_admin + node_id, {
		method: 'GET',
		asynchronous: true,
		parameters: {
			user_id: admin_id,
			rand: Math.random()
		},
		onSuccess: new Function('', this.var_name + '.removeAdminEntry(' + node_id + ', ' + admin_id + ')'),
		onFailure: new Function('', 'alert("не удалось удалить администратора");')
	});
}