var	REGEX_MSG_SYNTAX_ERROR			= "regex_error_syntax";
var	REGEX_MSG_INVALID_ELEMENT		= "regex_error_invalid_element";
var	REGEX_MSG_MISSING_ELEMENT		= "regex_warning_missing_element";
var	REGEX_MSG_ONLY_VIRTUAL			= "regex_warning_only_virtual_allowed";
var	REGEX_MSG_NO_VIRTUAL			= "regex_warning_no_virtual_allowed";

var	REGEX_OK								= 0;
var 	REGEX_WARNING						= 1;
var	REGEX_ERROR							= 2;

/* ********************************************************************************************************** */
/* Object Class */
/* ********************************************************************************************************** */

function joVMMDataModellerClass (id, gui_parent_node_name, gui_container_node_name, data_collection_name, data_name, source_data_types, destination_data_types)
{
	this.id								= id;
	this.gui_type						= "modeller";
	this.gui_title_node				= null;
	this.data_collection_name		= data_collection_name;
	this.data_collection			= null;
	this.data_name						= data_name;
	this.data_id						= null;
	this.data							= null;
	this.gui_modeller_height		= 0; // ??? (this.data_name == "object") ? 344 : 120;
	this.max_entity_height			= 0;
	
	this.landscape						= (this.data_name == "object" ? false : true);
	this.device							= (this.data_name == "device" ? true: false);
	this.destination_data_types	= destination_data_types;
	this.source_data_types			= source_data_types;
	
	this.gui_parent_node_name		= gui_parent_node_name;
	this.gui_parent_node			= joFIND (gui_parent_node_name);
	this.gui_container_node_name	= gui_container_node_name;
	this.gui_container_node		= joFIND (gui_container_node_name);
	
	this.data_entity_id				= "";
	this.data_entity					= null;
	
	this.form_node						= null;
	this.entity_form_nodes			= new Array();
	this.entity_form_id				= null;
	this.gui_body_node				= null;
	
	this.InitGui						= _joVMMDataModeller_InitGui;

	this.ResetData						= _joVMMDataModeller_ResetData;
	this.SetData						= _joVMMDataModeller_SetData;
	this.ShowData						= _joVMMDataModeller_ShowData;

	this.AddButton						= _joVMMDataModeller_AddButton;
	this.ActivateButtons			= _joVMMDataModeller_ActivateButtons;
	this.SetButtonState				= _joVMMDataModeller_SetButtonState;
	this.ActivateForms				= _joVMMDataModeller_ActivateForms;
	
	this.WriteRegex					= _joVMMDataModeller_WriteRegex;
	this.ValidateRegex				= _joVMMDataModeller_ValidateRegex;
	this.ExecRegex						= _joVMMDataModeller_ExecRegex;
	
	this.Action							= _joVMMDataModeller_Action;

	this.ShowDataEntity				= _joVMMDataModeller_ShowDataEntity;
	this.SetActiveEntity			= _joVMMDataModeller_SetActiveEntity;
	this.AddEntity						= _joVMMDataModeller_AddEntity;
	this.RemoveActiveEntity		= _joVMMDataModeller_RemoveActiveEntity;
	this.RemoveAllEntities			= _joVMMDataModeller_RemoveAllEntities;
	this.MoveActiveEntity			= _joVMMDataModeller_MoveActiveEntity;
	this.ModelActiveEntity			= _joVMMDataModeller_ModelActiveEntity;

	// events
	this.OnEvent		 				= _joVMMDataModeller_OnEvent;
	this.OnUpdateFormStatus		= _joVMMDataModeller_OnUpdateFormStatus;

	// do now
	this.InitGui();
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_Action (action_type)
{
	joVMMApp.GuiObjectAction (action_type, this); 
	return false;
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ModelActiveEntity()
{
	var entity 		= joVMM.entities[this.data_entity.name].FindNodeByAttribute ("", "id", this.data_entity.GetAttribute ("id", ""));
	var entity_id 	= entity.GetAttribute ("id");
	var scenario 	= joVMMApp.GetGuiObject ("vmm_" + entity.name + "_scenario");
	if (scenario) scenario.SetActiveEntity (scenario.id + "_" + entity_id, false);
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_InitGui()
{
	var button_node;
	var modeller_node, title_node, body_node, div_node, table_node, tr_node, body_tr_node, scenario_td_node, td_node;
	var tr_body_node, footer_node, td_node_form, form_title_node, form_node, input_node;

	modeller_node												= joAddHTMLChildNode  (this.gui_parent_node, "DIV", "vmm_modeller", this.id);
		title_node												= joAddHTMLChildNode  (modeller_node, "DIV", "title");
		
			this.gui_title_node								= joVMMApp.AddTitle   (title_node, this.data_name + "_" + this.gui_type, joGUI.GetLocaleText (this.gui_type + "_" + this.data_name));
			
		body_node												= joAddHTMLChildNode  (modeller_node, "DIV", "content");
			table_node											= joAddTableChildNode (body_node);
				body_tr_node									= joAddHTMLChildNode  (table_node, "TR");
					td_node										= joAddHTMLChildNode  (body_tr_node, "TD");
						div_node									= joAddHTMLChildNode  (td_node, "DIV", "scenario", this.data_name + "_modeller_resize");
							table_node							= joAddTableChildNode (div_node, "", this.data_name + "_modeller_resize_table");
								tr_node							= joAddHTMLChildNode  (table_node, "TR");
									scenario_td_node			= joAddHTMLChildNode  (tr_node, "TD", "vmm_modeller_scenario");
										this.gui_body_node	= joAddHTMLChildNode  (scenario_td_node, "DIV", "", this.id + "_modeller");
		this.form_node	 										= joAddHTMLChildNode  (modeller_node, "FORM", "footer");
			joAddEvent 											(this.form_node, "submit", function (event) { void(0); });
			regex_node											= joAddHTMLChildNode  (this.form_node, "DIV", "regex");
				table_node										= joAddTableChildNode (regex_node);
					tr_node										= joAddHTMLChildNode  (table_node, "TR");
						td_node									= joAddHTMLChildNode  (tr_node, "TD", "regex_input");
							input_node							= joAddHTMLChildNode  (td_node, "input", "regex_input", this.data_name + "_regex");
								joDOM.SetAttribute 			(input_node, "vmm_data_obj", this.id);
								joAddEvent 						(input_node, "keyup", function (event) 
																	{ 
																		var event_node 	= (window.event) ? event.srcElement : objSrc = event.target;	// browser incompatibilities
																		var modeller_obj 	= joVMMApp.GetGuiObject (event_node.getAttribute ("vmm_data_obj"));
																		modeller_obj.ValidateRegex (false); 
																		return false; 
																	});
						td_node									= joAddHTMLChildNode  (tr_node, "TD", "regex_go");
			footer_node											= joAddHTMLChildNode  (this.form_node, "DIV", "footer");
	
	//...................add source element attribute forms
	td_node_form 				= joAddHTMLChildNode (body_tr_node, "TD", "vmm_modeller_form");
	
	for (var i = 0; i < this.source_data_types.length; i++)
	{
		form_node	 		= joAddHTMLChildNode (td_node_form, "FORM", "vmm_modeller_form");
		form_node.action = "";
		form_node.method = "";
			joAddEvent 		(form_node, "submit", function (event) { void(0); });
		joDOM.SetAttribute (form_node, "name", this.id + "_" + this.source_data_types[i] + "_form");
		joDOM.SetAttribute (form_node, "vmm_data_name", this.source_data_types[i]);
		joDOM.SetAttribute (form_node, "vmm_gui_name", this.id);

		form_title_node	= joAddHTMLChildNode (form_node, "P", "vmm_modeller_form_title");
		form_title_node.innerHTML = joGUI.GetLocaleText (this.source_data_types[i]);
		
		joGUI.BuildForm (this.source_data_types[i] + "_body_element", form_node);
		this.entity_form_nodes.push (form_node);
	}

	this.AddButton (td_node, "regex");
	
	if (this.landscape)
	{
		this.AddButton (footer_node, "model");
	}
	
	this.AddButton (footer_node, "delete");
	
	if (this.landscape)
	{
		this.AddButton (footer_node, "first");
		this.AddButton (footer_node, "left");
		this.AddButton (footer_node, "right");
		this.AddButton (footer_node, "last");
	}
	else
	{
		this.AddButton (footer_node, "top");
		this.AddButton (footer_node, "up");
		this.AddButton (footer_node, "down");
		this.AddButton (footer_node, "bottom");
	}

	this.entity_form_id = this.entity_form_nodes[0];
	this.ActivateButtons();
	this.ActivateForms();
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_AddButton (parent_node, button_type)
{
	button_node						= joAddHTMLChildNode (parent_node, "BUTTON", "joGfxButton");
	button_node.name				= "button_" + button_type;
	button_node.title				= joGUI.GetLocaleText (button_type);
	button_node.onclick			= function() 
										{ 
											var gui_obj = joVMMApp.GetGuiObject (this.getAttribute ("vmm_data_obj"));
											gui_obj.Action (this.getAttribute ("name").substr ("button_".length)); 
											return false; 
										};
	joDOM.SetAttribute (button_node, "vmm_data_obj", this.id);
	
	joAddImageChildNode (button_node, 	"images/button_" + button_type + ".gif");
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ActivateForms()
{ 
	var form_count = this.entity_form_nodes.length;
	var curr_form_name = (this.data_entity ? this.id + "_" + this.data_entity.name + "_form" : "");
	for (var i = 0; i < form_count; i++)
	{
		this.entity_form_nodes[i].style.display = (this.entity_form_nodes[i].name == curr_form_name ? "block" : "none");
	}
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ActivateButtons()
{
	var active_entity	 = (this.data_id == "" || !this.data_id) ? false : true;
	
	this.SetButtonState ("button_delete", this.data_entity);
	if (this.landscape)
	{
		this.SetButtonState ("button_last", this.data_entity && this.data_entity.element_next);
		this.SetButtonState ("button_right", this.data_entity && this.data_entity.element_next);
		this.SetButtonState ("button_left", this.data_entity && this.data_entity.element_prev);
		this.SetButtonState ("button_first", this.data_entity && this.data_entity.element_prev);
	}
	else
	{
		this.SetButtonState ("button_top", this.data_entity && this.data_entity.element_next);
		this.SetButtonState ("button_up", this.data_entity && this.data_entity.element_next);
		this.SetButtonState ("button_down", this.data_entity && this.data_entity.element_prev);
		this.SetButtonState ("button_bottom", this.data_entity && this.data_entity.element_prev);
	}
	
	var regex_input 							= joFIND (this.data_name + "_regex");
	regex_input.readOnly 					= (this.data ? "" : "readonly");
	regex_input.style.backgroundColor 	= (this.data ? "" : "#ECE9D8");
	
	this.SetButtonState ("button_edit", this.data);
	this.SetButtonState ("button_model", this.data && this.data_entity);

	//.............................regex input field title tag contains error message: title == "" --> no error!

	this.SetButtonState ("button_regex", this.data && regex_input.title == ""); 
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_SetButtonState (button_name, state)
{
	var button_node = joDOM.GetFormElement (this.form_node, button_name);
	if (button_node)
	{
		/*
		button_node.style.visibility = state ? "visible" : "hidden";
		*/
		button_node.style.cursor = state ? "pointer" : "auto";
		button_node.disabled = state ? "" : "disabled";
		button_node.style.backgroundColor = state ? "" : "#ECE9D8";
		
		var image = joDOM.GetSubNode (button_node, "IMG");
		if (image)
		{
			image.style.visibility = state ? "visible" : "hidden";
		}
	}
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ResetData()
{
	this.data_collection = null;
	this.data = null;
	this.data_id = null;
	this.data_entity_id = "";
	this.data_entity = null;
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_SetData (data_id)
{
	if (!this.data_collection)
	{
		this.data_collection = joVMM.xmlData.FindNode (this.data_collection_name);
	}
	
	this.gui_title_node.innerHTML = joGUI.GetLocaleText (this.gui_type) + ((data_id != '') ? ": " + (this.data_collection_name == "device" ? joGUI.GetLocaleText (data_id)  : data_id) : "");

	this.data_id	= data_id;
	this.data 		= (this.data_collection_name == "device")
						?
						this.data_collection.FindNodeByAttribute ("", "id", this.data_id)
						:
						this.data_collection.FindNodeByAttribute (this.data_name, "id", this.data_id);
	
	var is_virtual = (this.data ? joIsBoolean (this.data.GetAttribute ("is_virtual")) : false);
	
	/* ??? pml parameter
	for (var i = 0; i < this.entity_form_nodes.length; i++)
	{
		var pml_field = joDOM.GetFormElement (this.entity_form_nodes[i], "pml_parameter");
		if (pml_field)
		{
			pml_field.disabled = !is_virtual;
			pml_field.style.backgroundColor = is_virtual ? "" : "#ECE9D8";
		}
	}
	*/
	
	this.ShowData();
	this.data_entity_id = "";
	this.data_entity = null;
	
	this.WriteRegex();
	this.ActivateButtons();
	this.ActivateForms();
	
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ShowData (data_id)
{
	if (!this.data_collection && joVMM && joVMM.xmlData)
	{
		this.data_collection = joVMM.xmlData.FindNode (this.data_collection_name);
	}
		
	if (this.data_collection)
	{
		// init
		joDOM.RemoveChildNodes (this.gui_body_node);
		this.data_id 		= (data_id) ? data_id : this.data_id;
		this.data 			= this.data_collection.FindNodeByAttribute ("", "id", this.data_id);
		this.element_id	= "";
		
		if (this.landscape && this.data)
		{
			this.max_entity_height = 0;
			if (this.data.childNodes && this.data.childNodes.element_first)
			{
				try 
				{
					this.max_entity_height	= Math.ceil (joVMM.GetHighestEntityHeight (this.data.childNodes.element_first.FindNode ("body", false)));
				}
				catch (e)
				{
				}
			}
		}
		
		// fill
		if (this.data)
		{
			var data_body = this.data.FindNode ("body");
			var element_id;
			var data_entity;
			if (data_body)
			{
				var element 		= this.landscape ? data_body.childNodes.element_first : data_body.childNodes.element_last;
				var position 		= this.landscape ? 0 : data_body.childNodes.GetElementCount() - 1;
				var parent_node 	= this.gui_body_node;
				
				if (this.landscape)
				{
					var table_obj = joAddTableChildNode (this.gui_body_node);
					parent_node = joAddHTMLChildNode (table_obj, "TR");
				}

				while (element)
				{
					element_id = element.GetAttribute ("id");
					data_entity = joVMM.entities[element.name].FindNodeByAttribute (element.name, "id", element_id);

					if (data_entity)
					{					
						this.ShowDataEntity (element, data_entity, position, parent_node);
					}
					position += (this.landscape ? 1 : -1);
					element = this.landscape ? element.element_next : element.element_prev;
				}
			}
		}
	}

	this.WriteRegex();
	this.ActivateButtons();
	this.ActivateForms();
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_WriteRegex()
{
	var regex_input 		= joFIND (this.data_name + "_regex");
	regex_input.value 	= "";
	var value 				= "";
	var attrib;
	var attrib_complex;

	if (this.data)
	{
		var data_body 		= this.data.FindNode ("body");
		if (data_body)
		{
			var element 		= data_body.childNodes.element_first;
			while (element)
			{
				if (value != "") value += ";";
				element_id = element.GetAttribute ("id");
				switch (element.name)
				{
					case "material":
						attrib 				= element.GetAttribute ("thickness");
						attrib_complex		= (attrib.indexOf ("(") == -1 ? false : true);
						value 				+= element_id + (attrib_complex ? "" : "(") + attrib + (attrib_complex ? "" : ")");
						break;
					case "object":
						attrib				= element.GetAttribute ("width");
						attrib_complex		= (attrib.indexOf ("(") == -1 ? false : true);
						value 				+= element_id + (attrib_complex ? "" : "(") + attrib + (attrib_complex ? "" : ")");
						break;
					case "macro":
						var count = parseInt (element.GetAttribute ("count"));
						value += "$" + element_id;
						if (count > 1) value += "{" + count + "}";
						break;
				}
	
				element = element.element_next;
			}
			regex_input.value = value;
		}
		this.ValidateRegex (false);
	}
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ExecRegex()
{
	if (this.ValidateRegex (false))
	{
		this.data_entity_id				= "";
		this.data_entity					= null;
		this.ValidateRegex (true);
	}
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ValidateRegex (build_body)
{
	var result 				= false;
	
	if (this.data)
	{
		var regex_input 		= joFIND (this.data_name + "_regex");
		var checkval 			= regex_input.value.replace (/\s*/g, "");
		var elems				= checkval.split (";");
		var count 				= elems.length;
		var field_style		= joGUI_COL_FIELD_VALUE_OK;
		var is_virtual			= joNumCheck (this.data.GetAttribute ("is_virtual"), "boolean");
		var body_obj 			= this.data.FindNode ("body", false);
		var regex_other		= is_virtual ? regex_complex : regex_float;
		var data_entity		= null;
		var regex_attrib;
		var new_entity;
		var new_entity_name;
		var new_attrib;
		var new_value;
	
		var elem;
		var elem_name;
		result = true;
	
		regex_input.title = "";
		//	if (checkval != "") // empty regexes are allowed now
		{
			try
			{
				if (build_body) this.RemoveAllEntities();
			
				for (var i = 0; i < count; i++)
				{
					elem = elems[i];
		
					//...............................................................................................MACRO
					if (elems[i].charAt(0) == "$")
					{
						elem 		= elem.substr (1);
						
						//....................................................................macros allowed only in device
						if (this.data_name != "device") throw ([REGEX_ERROR, REGEX_MSG_INVALID_ELEMENT, elem]);
						
						result 					= (elem.search (regex_macro) == -1 ? false : true);
						
						//...................................................................................no valid macro
						if (!result && checkval != "") throw ([REGEX_ERROR, REGEX_MSG_SYNTAX_ERROR, elem]); // empty regexes are allowed now
						
						elem_name 				= regex_name.exec (elem)[0];
						new_entity_name 		= "macro";
						new_attrib 				= "count";
						var data_entity 		= joVMM.entities["macro"].FindNodeByAttribute ("", "id", elem_name);
						
						//........................................................................referred entity not found
						if (!data_entity) throw ([REGEX_WARNING, REGEX_MSG_MISSING_ELEMENT, elem_name]);
	
						if (build_body) 
						{	
							new_entity 			= body_obj.AppendNode (new_entity_name);
							new_entity.SetAttribute ("id", elem_name);
							
							regex_attrib 		= new RegExp ("\\{.*\\}");
							new_value			= regex_attrib.exec (elem);
							if (new_value)
							{
								new_value		= new_value[0];
								new_value		= new_value.substr (1, new_value.length - 2);
								new_entity.SetAttribute (new_attrib, new_value);
							}
							else
							{
								new_entity.SetAttribute (new_attrib, "1");
							}
						}	
					}
					//..................................................................................OBJECT OR MATERIAL
					else
					{
						result 						= (elem.search (regex_other) == -1 ? false : true);
						
						//..................................................................................no valid entity
						if (result)
						{
							new_entity_name 		= (this.data_name == "object" ? "material" : "object");
							new_attrib 				= (this.data_name == "object" ? "thickness" : "width");
							
							elem_name 				= regex_name.exec (elem)[0];
							data_entity 			= joVMM.entities[new_entity_name].FindNodeByAttribute ("", "id", elem_name);
							
							//........................................................................referred entity not found
							if (!data_entity) throw ([REGEX_WARNING, REGEX_MSG_MISSING_ELEMENT, elem_name]);
		
							if (build_body) 
							{	
								if (!body_obj)
								{
									body_obj = this.data.FindNode ("body", true);
								}
	
								new_entity 		= body_obj.AppendNode (new_entity_name);
								new_entity.SetAttribute ("id", elem_name);
								
								regex_attrib 	= new RegExp ("\\(.*\\)");
								new_value		= regex_attrib.exec (elem)[0];
								
								//for float numbers: omit brackets
								if (new_value.indexOf (",") == -1) 	new_value = new_value.substr (1, new_value.length - 2);
								
								new_entity.SetAttribute (new_attrib, new_value);
							}	
						}
						else
						{
							result = true;
							if (checkval != "") throw ([REGEX_ERROR, REGEX_MSG_SYNTAX_ERROR, elem]);  // empty regexes are allowed now
						}
					}
					
					//...................................................................................check virtuality
					if (data_entity && this.data_name != "object")
					{
						var data_entity_is_virtual = joNumCheck (data_entity.GetAttribute ("is_virtual", "false"), "boolean");
						
						//..........................................................................wrong virtuality state
						if (data_entity_is_virtual)
							throw ([REGEX_WARNING, (REGEX_MSG_NO_VIRTUAL), elem_name])
					}
				}
				regex_input.style.cursor = "auto";
	
				if (build_body)
				{
					this.ShowData();
					joVMMApp.RefreshScenarios (this.data_name);
				}
			}
			//..............................................................................................catch error
			catch (e)
			{
				if (e[0])
				{
					field_style = (e[0] == REGEX_ERROR ? joGUI_COL_FIELD_VALUE_INVALID : joGUI_COL_FIELD_VALUE_WARNING);
					regex_input.title = joGUI.GetLocaleText (e[1]) + ": " + e[2];
					regex_input.style.cursor = "help";
					result = false;
				}
				else if (joDEBUG) 
				{ 
		//			alert (e); 
				}
			}
			
			this.SetButtonState ("button_regex", result);
		}
		
		regex_input.style.backgroundColor = field_style;
	}
	
	return result;
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_ShowDataEntity (entity_object, entity_class, entity_object_number, parent_id)
{
	var element_id, element_node;
	var element_height;
	var element_info;
	
	if (this.landscape)
	{
		parent_id 			= joAddHTMLChildNode (parent_id, "TD");
		element_height		= joVMM.GetObjectHeight (entity_class);
	}
	
	switch (entity_class.name)
	{
		case "material":
			element_info = entity_object.GetAttribute ("thickness");
			break;
		case "object":
			element_info = entity_object.GetAttribute ("width");
			break;
		case "macro":
			element_info = "x" + entity_object.GetAttribute ("count");
			break;
	}
	
	element_id										= entity_class.GetAttribute ("id");
	element_node 									= joAddHTMLChildNode (parent_id, "DIV", "vmm_modeller_element element_" + entity_class.name, this.id + "_" + entity_object_number);
	element_node.innerHTML 					= "<SPAN>" + element_info + "</SPAN>";
	element_node.style.backgroundColor 	= entity_class.GetAttribute ("gui_color", "#ffffff");
	element_node.style.backgroundImage		= "URL(images/bg_" + entity_class.name + ".gif)";
	
	
	element_node.title 							= element_id + " [" + element_info + "]";

	element_node.onclick 						= function() { joVMMApp.GetGuiObject (this.getAttribute ("vmm_data_obj")).SetActiveEntity (this.id); return false; };
	element_node.ondblclick 					= function() { joVMMApp.GetGuiObject (this.getAttribute ("vmm_data_obj")).ModelActiveEntity(); return false; };
	//element_node.ondblclick 					= function() { joVMMApp.GetGuiObject (this.getAttribute ("vmm_data_obj")).RemoveActiveEntity(); return false; };

	if (this.landscape)
	{
		element_node.style.height 			= Math.round ((element_height / this.max_entity_height) * this.gui_modeller_height);
	}
	
	joDOM.SetAttribute (element_node, "vmm_data_obj", this.id);
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_AddEntity (source_data_gui_obj)
{
	var result = false;
	
	this.data 		= this.data_collection.FindNodeByAttribute ("", "id", this.data_id);
	
	if (this.data)
	{	
		// is type of 'source_data_gui_obj' one of my source elements?
		for (var i = 0; !result && i < this.source_data_types.length; i++)
		{
			if (this.source_data_types[i] == source_data_gui_obj.data_name) result = true;
		}
		
		// add 'source_data_gui_obj' to my source elements
		if (result)
		{
			var body_obj 			= this.data.FindNode ("body", true);
			var new_obj				= null;

			// dissolve macro when inserted into another macro			
			if (source_data_gui_obj.data_name == "macro" && !this.device)
			{
				var data_entity 	= joVMM.entities [source_data_gui_obj.data_name].FindNodeByAttribute (source_data_gui_obj.data_name, "id", source_data_gui_obj.data_id);
				
				if (data_entity.childNodes && data_entity.childNodes.element_first)
				{
					var sub_entity		= data_entity.childNodes.element_first.FindNode ("body", false).childNodes.element_first;
					var curr_node		= this.data_entity;
					while (sub_entity)
					{
						new_obj 	= body_obj.AppendNode (sub_entity.name, curr_node);
						new_obj.SetAttribute ("id", sub_entity.GetAttribute ("id"));
						new_obj.SetAttribute ("width", sub_entity.GetAttribute ("width"));
						curr_node = new_obj;
						sub_entity = sub_entity.element_next;
					}
				}
			}
			else // insert directly (object into macro/device or macro into device
			{
				new_obj 			 	= body_obj.AppendNode (source_data_gui_obj.data_name, this.data_entity);
				new_obj.SetAttribute ("id", source_data_gui_obj.data_id);
			}
			
			
			if (new_obj)
			{
				this.data_entity_id = "";
				this.data_entity = new_obj;
				
				if (source_data_gui_obj.data_name != "macro" && this.data_entity) joGUI.SaveForm (this.entity_form_id, this.data.FindNode ("body"), this.data_entity.name, this.data_entity, this.data_entity.name + "_body_element");
					
				if (!joNumCheck (this.data.GetAttribute ("is_virtual", "0"), "boolean")) // not a virtual entity
				{
					switch (new_obj.name)
					{
						case "material":
							new_obj.SetAttribute ("thickness", joGetRealPart (new_obj.GetAttribute ("thickness", "1")));
						break;
						case "object":
							new_obj.SetAttribute ("width", joGetRealPart (new_obj.GetAttribute ("width", "1")));
						break;
						case "macro":
							new_obj.SetAttribute ("count", joGetRealPart (new_obj.GetAttribute ("count", "1")));
						break;
					}
					
					joGUI.FillForm (this.entity_form_id, this.data_entity, new_obj.name + "_body_element");
				}
				else if (new_obj.name == "macro")
				{
					new_obj.SetAttribute ("count", joGetRealPart (new_obj.GetAttribute ("count", "1")));
				}
	
				this.ShowData();
				this.SetActiveEntity (this.id + "_" + body_obj.childNodes.GetElementPosition (new_obj), true);
	
				joVMMApp.RefreshScenarios (this.data_name);
				result = true;
			}
			
		}
	}
	return result;
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_MoveActiveEntity (move_type)
{
	var body_obj 	= this.data.FindNode ("body", true);
	switch (move_type)
	{
		case "top":
		case "last":
			body_obj.childNodes.MoveElementToEnd (this.data_entity);
		break;
		case "up":
		case "right":
			body_obj.childNodes.SwapElements (this.data_entity, this.data_entity.element_next);
		break;
		case "down":
		case "left":
			body_obj.childNodes.SwapElements (this.data_entity, this.data_entity.element_prev);
		break;
		case "bottom":
		case "first":
			body_obj.childNodes.MoveElementToStart (this.data_entity);
		break;
	}
	var active_entity = this.data_entity;
	this.data_entity = null;
	this.data_entity_id = "";
	this.ShowData();
	this.SetActiveEntity (this.id + "_" + body_obj.childNodes.GetElementPosition (active_entity), true);
	joVMMApp.RefreshScenarios (this.data_name);
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_RemoveAllEntities()
{
	var body_obj 		= this.data.FindNode ("body", true);
	var entities		= body_obj.childNodes;
	if (entities)
	{
		entities.RemoveAllElements();
	}
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_RemoveActiveEntity()
{
	var body_obj 	= this.data.FindNode ("body", true);
	
	var active_entity = null;
	
	if (this.data_entity.element_prev) 
		active_entity = this.data_entity.element_prev;
	else if (this.data_entity.element_next)
		active_entity = this.data_entity.element_next;

	body_obj.childNodes.RemoveElement (this.data_entity);
	this.data_entity = null;
	this.data_entity_id = "";
	this.ShowData();
	
	if (active_entity)
		this.SetActiveEntity (this.id + "_" + body_obj.childNodes.GetElementPosition (active_entity), true);
	
	joVMMApp.RefreshScenarios (this.data_name);
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_SetActiveEntity (gui_id, recently_added, just_refresh)
{
	var gui_node;
	
	// adapt gui
	if (this.data_entity_id != "" && !just_refresh)
	{
		gui_node = joFIND (this.id + "_" + this.data_entity_id);
		if (gui_node) gui_node.className = gui_node.className.substr (0, gui_node.className.indexOf (" active"));
	}
	
	gui_node = joFIND (gui_id);
	if (gui_node) gui_node.className += " active";
	
	// set data
	this.data_entity_id	 	= gui_id.substr (this.id.length + 1);
	this.data_entity 			= this.data.FindNode("body").childNodes.GetElement (this.data_entity_id);
	this.entity_form_id 	= joDOM.GetForm (this.id + "_" + this.data_entity.name + "_form");

	// fill data entity form
	if (!recently_added) 
		joGUI.FillForm (this.entity_form_id, this.data_entity, this.data_entity.name + "_body_element");
		
	this.ActivateButtons();
	this.ActivateForms();
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_OnUpdateFormStatus (form_node, form_status, field_node) 
{ 
	if (field_node && this.data_entity)
	{
		if (this.data)
		{
			var is_virtual = joNumCheck (this.data.GetAttribute ("is_virtual"), "boolean");
			if (!is_virtual)
			{
				form_status = joNumCheck (field_node.value, "posfloat");
				if (!form_status) 
					joGUI.ShowFieldValidState (field_node, false, false);
			}
		}
					
		// save field data
		if (form_status)
		{
			var field_name = field_node.name;
	
			if (field_node.value != this.data_entity.GetAttribute (field_name, ""))
			{
				this.data_entity.SetAttribute (field_name, field_node.value);
				joVMMApp.RefreshScenarios (this.data_name);
				this.ShowData();
				this.SetActiveEntity (this.id + "_" + this.data_entity_id, false, true);
			}
		}
	}
}

/* ********************************************************************************************************** */

function _joVMMDataModeller_OnEvent (event_type, event_data_type)
{
	switch (event_type)
	{
		case JO_VMM_GUI_ON_RESIZE:
			this.gui_modeller_height = joGetDivInfo (joFIND (this.data_name + "_modeller_resize"), "height") - 6;
			this.ShowData();
		break;
	}
}

