var Dependencies = new Object();
var Dependencies_By_Namespace = new Object();

var Focused_Action_Namespace = "";
var Last_Path = "";
var Last_Path_Item_Namespace;
var Default_Path = "Site/Default_Page";
					
var Directory;
//TODO: Directory_Set
var Directory_Set = false;

var Fills = new Object();

function Start_Jelly(New_Directory)
{
	Directory = New_Directory;
	Directory_Set = true;
		
	// Watch address bar
	setInterval("Watch_Address_Bar();", 100);
	
	// Write flash element for special case features.
	Body_Item = $("Body");
	Flash_Container_Item = Generate_Control("Flash");
	Body_Item.appendChild(Flash_Container_Item);
	var Jelly_Flash_Extras_Writer = new SWFObject("/jelly/Flash/Jelly_Flash_Extras.swf", "Jelly_Flash_Extras", "1", "1", "9");
	Jelly_Flash_Extras_Writer.addVariable("wmode", "transparent");
	Jelly_Flash_Extras_Writer.write("Jelly_Flash_Extras_Container");
		
	// Add document level listener for generic click handling.
	document.addEventListener('click', Handle_Document_Click, true);
}

// Gather namespaced parameters from the document and submit an ajax request.
function Execute_Action(Namespace)
{
	// Prepare namespace and action parameters.
	var Parameters = new Object();
	Parameters["Namespace"] = Namespace;
	Parameters["Action"] = $F(Namespace + "_Action");
	Parameters["Parameters"] = new Object();
	
	// Gather parameters from namespaced input elements.
	var Inputs = document.getElementsByTagName("input");
	for (InputIndex = 0; InputIndex < Inputs.length; InputIndex++)
	{
		if (Inputs[InputIndex].id.substring(0, Namespace.length) == Namespace)
		{
			if (Inputs[InputIndex].type == "checkbox")
			{
				if (Inputs[InputIndex].checked)
					Parameters["Parameters"][Inputs[InputIndex].name] = "1";
				else
					Parameters["Parameters"][Inputs[InputIndex].name] = "0";
			}
			else
				Parameters["Parameters"][Inputs[InputIndex].name] = $F(Inputs[InputIndex]);
		}
	}
		
	// Gather parameters from namespaced select elements.
	var Inputs = document.getElementsByTagName("select");
	for (InputIndex = 0; InputIndex < Inputs.length; InputIndex++)
		if (Inputs[InputIndex].id.substring(0, Namespace.length) == Namespace)
			Parameters["Parameters"][Inputs[InputIndex].name] = $F(Inputs[InputIndex]);
	
	
	// Gather parameters from namespaced textarea elements.
	var Inputs = document.getElementsByTagName("textarea");
	for (InputIndex = 0; InputIndex < Inputs.length; InputIndex++)
		if (Inputs[InputIndex].id.substring(0, Namespace.length) == Namespace)
			Parameters["Parameters"][Inputs[InputIndex].name] = $F(Inputs[InputIndex]);			

	// Submit an AJAX request
	Execute_Action_With_Parameters(Parameters);
}

// Submit an AJAX request with the given parameters.
function Execute_Action_With_Parameters(Parameters)
{
	// TODO: ?
	if (Parameters["Namespace"])
		Namespace = Parameters["Namespace"];
	else
		Namespace = "Action";
		
	// Show named "_Loading" status indicator for the action, if it exists.
	var LoadingElement = $(Namespace + "_Loading");
	if (LoadingElement)
		LoadingElement.style.display = "inline";		
	
	// Prepare POST parameters.
	Post_Parameters = "";
	Post_Parameters += "&" + "Submit_Action" + "=" + Parameters["Action"];
	Post_Parameters += "&" + "Submit_Namespace" + "=" + Namespace;
	Post_Parameters += "&" + "Submit_Method" + "=" + "AJAX";
	
	for (Parameter_Index in Parameters["Parameters"])
	{
		Post_Parameters += "&" + (Namespace + "_" + Parameter_Index) + "=" + encodeURIComponent(Parameters["Parameters"][Parameter_Index]);
		Post_Parameters = Post_Parameters.replace("+", "%2B");
	}
		
	// Submit form data to the server asynchronously.
	AJAX_Request(
		{
			URL: Directory + "/",
			Post_Variables: Post_Parameters,
			Callback: function (HTTP_Request)
				{	
					var Result = HTTP_Request.responseText;
					
					// Display response in the named "_Result" container for this action, if it exists.
					var Result_Div = $(Namespace + "_Result");
					if (Result_Div)
						Result_Div.innerHTML = Result;
						
					// Evaluate response javascript, including registering changed items.			
					Execute_Scripts(Result);						
					
					// Hide named "_Loading" status indicator for this action, if it exists.
					var LoadingElement = $(Namespace + "_Loading");
					if (LoadingElement)
						LoadingElement.style.display = "none";
						
					// Update elements dependent on changed items.
					Refresh_Fills();
				}
		}
	)
}

// Register action in focus.
function Set_Focused_Action_Namespace(Namespace)
{
	Focused_Action_Namespace = Namespace;
}

// Execute focused action. (for example, on enter key)
function Execute_Focused_Action()
{
	Execute_Action(Focused_Action_Namespace);
}

// Execute focused action on enter key.
function Handle_Input_Key_Down(e)
{
	var code;
	if (!e) var e = window.event;
	if (e.keyCode) code = e.keyCode;
	else if (e.which) code = e.which;
	var character = String.fromCharCode(code);
	if (code == 13)
	{
		Execute_Focused_Action();
		return false;
	}
	return true;
}

// Check for path changes after the anchor character and register change.
function Watch_Address_Bar()
{
	// Parse path by reading the URL after the anchor character and trimming the trailing backslash.
	var Current_Path = document.location.href;
	if (Current_Path.substring(Current_Path.length - 1) == "/")
		Current_Path = Current_Path.substring(0, Current_Path.length - 1);		
	var Pound_Position = Current_Path.indexOf("#");
	if (Pound_Position > -1)
		Current_Path = Current_Path.substring(Pound_Position + 1);
	
	// If no path is specified, set a default path.
	if (Current_Path == "" || Pound_Position == -1)
		Current_Path = Default_Path;
	
	// If the URL has changed since last check, register change.
	if (Current_Path != Last_Path)
	{
		Last_Path = Current_Path;
		
		// Clean current path, set a default path if necesary.
		Current_Path = Current_Path.replace(/\/*$/g, "");		
		if (Current_Path == "")
			var Request_URL = Default_Path;
		else
			var Request_URL = Current_Path;
		
		// Register changed URL.
		Item_Changed("Last_Path_Item");
		
		// Refresh elements dependent on the URL.
		Refresh_Fills();
	}
}

// Set the address bar to the new path with a preceding anchor character.
function Set_Location(New_Path)
{
	var Pound_Position = New_Path.indexOf("#");
	if (Pound_Position != -1)
		New_Path = New_Path.substring(Pound_Position + 1);
	document.location.href = "#" + New_Path;
}

// Evaluate all <script></script> tags in text chunk.
function Execute_Scripts(HTML_Text)
{
	Get_Script_Tags_From_HTML_Text = new RegExp('<script[^>]*>([^]*?)<\/script>', 'img');
	Get_Code_From_Script_Tag = new RegExp('<script[^>]*>([^]*?)<\/script>', 'im');
	Script_Tags = (HTML_Text.match(Get_Script_Tags_From_HTML_Text));
	for (var Script_Tag_Index in Script_Tags)
	{
		// TODO: Not sure why, but a naming conflict with prototype returns the function Enumerable at the end of the array returned by match.
		// I wrote a typeof check to ensure compatibility with prototype, but this is weird.
		if (typeof (Script_Tags[Script_Tag_Index]) == "string")
			eval(("" + Script_Tags[Script_Tag_Index]).match(Get_Code_From_Script_Tag)[1]);
	}
}


// Form a POST request to the server and run a Callback function on response.
function AJAX_Request(Parameters)
{
	var URL = Parameters["URL"];
	var Callback = Parameters["Callback"];
	var Post_Variables = Parameters["Post_Variables"];
	var Target_Element = Parameters["Target_Element"];
	if (!Target_Element) Target_Element = $(Parameters["Target_Element_ID"]);
		
	var HTTP_Request = new XMLHttpRequest();
	HTTP_Request.open("POST", URL, true)
	HTTP_Request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
	HTTP_Request.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
	HTTP_Request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8');
	HTTP_Request.onreadystatechange = function() 
	{
		if (HTTP_Request.readyState == 4 && HTTP_Request.status == 200)
		{
			Callback.call(null,HTTP_Request);
		}
	};
	HTTP_Request.send(Post_Variables);
}

// Fill a target element with a url by performing an AJAX request, performing transition effects, and registering dependencies.
function Fill(Parameters)
{
	var Response_Function = Parameters["Response"];
	var Show_Loading = Parameters["Show_Loading"];
	var Transition = Parameters["Transition"];	
	var Not_From_Last_Path_Item = Parameters["Not_From_Last_Path_Item"];
	var Target_Element = Parameters["Element"];
	var Destination_URL = Parameters["URL"];
	if (!Target_Element) Target_Element = $(Parameters["ID"]);
	
	// Parse URL into location, item, and parameters.
	var URL_Parts = Destination_URL.split("/");
	Location_URL = (URL_Parts.slice(0,URL_Parts.length - 1) || []).join("/");
	URL_Parts = URL_Parts.pop().split(":");
	Item_URL = URL_Parts[0];
	Parameter_URL = URL_Parts[1];
		
	// Process location.
	if (!Location_URL)		
	   Location_URL = Directory;
	  
	// Process parameters.
	if (Not_From_Last_Path_Item)
		Parameter_URL += ",From_Last_Path_Item=1";
	
	// Rebuild URL.
	Destination_URL = Location_URL + "/" + Item_URL + ":" + Parameter_URL;		
	
	// Register dependency between element and URL.
	Add_Dependency({"Namespace": Target_Element.id, "URL": Destination_URL});	
	
	// Begin size transition effect.
	if (Transition != "None")
	{
		var Original_Display = Target_Element.style.display;
		Target_Element.style.display = "block";
		
		var Start_Width = Target_Element.offsetWidth;
		var Start_Height = Target_Element.offsetHeight;
		
		var Original_Overflow = Target_Element.style.overflow;
		Target_Element.style.overflow = "hidden";
		var Original_Height = Target_Element.style.height;
		Target_Element.style.height = Start_Height + "px";
		
		var Destination_Element = document.createElement("div");
		Destination_Element.id = Target_Element.id + "_Content";
		Destination_Element.style.position = "absolute";
		Destination_Element.style.visibility = "hidden";
		Destination_Element.style.width = Target_Element.offsetWidth + "px";
		Target_Element.parentNode.insertBefore(Destination_Element, Target_Element);
	}	
	
	// Begin loading transition effect.
	if (Show_Loading != "False")
	{
		var Loading_Element = $(Target_Element.id + "_Loading");
		if (!Loading_Element)
		{
			Loading_Element = Generate_Control("Loading");
			Loading_Element.id = Target_Element.id + "_Loading";
		}		
		Loading_Element.style.width = Math.max(Target_Element.scrollWidth, 10) + "px";
		Loading_Element.style.height = Math.max(Target_Element.scrollHeight, 10) + "px";
		Target_Element.parentNode.insertBefore(Loading_Element, Target_Element);	
	}
	
	// Perform AJAX request
	AJAX_Request(
		{
			URL: Destination_URL,			
			Callback: function (Request_Object)
				{
					var Result = Request_Object.responseText;
			
					// Fill target element.
					Target_Element.innerHTML = Result;
					
					// Finish loading transition effect.
					if (Show_Loading != "False")
						Loading_Element.parentNode.removeChild(Loading_Element);					
						
					// Finish size transition effect.
					if (Transition != "None")
					{
						Destination_Element.style.width = Target_Element.offsetWidth + "px";
						Destination_Element.innerHTML = Result;
						var End_Width = Destination_Element.offsetWidth;
						var End_Height = Destination_Element.offsetHeight;
						Destination_Element.parentNode.removeChild(Destination_Element);
						
						Ramp_Value({"Command": "$('" + Target_Element.id + "').style.height = VALUE + 'px';", "Start_Value": Start_Height, "End_Value": End_Height, "On_Complete": function () {Target_Element.style.overflow = Original_Overflow; Target_Element.style.height = Original_Height; Target_Element.style.display = Original_Display;}});
					}
					
					// Execute embedded javascript.
					Execute_Scripts(Result);
					
					// Execute callback function.
					if (Response_Function)
						Response_Function.call();
				}
		}
	);

}


// Refresh elements that are registered for updates and their dependent elements.
function Refresh_Fills()
{
	for (Fill_Index in Fills)
	{
		var Updates = "";
		Fill_Element = Fills[Fill_Index];
		
		var Dependency = Fill_Element;
		var Already = false;
		while (Dependency)
		{
			if (Dependency["Parent_Namespace"])
			{
//				console.log(Dependency["Parent_Namespace"]);
				Dependency = Dependencies_By_Namespace[Dependency["Parent_Namespace"]];
			}
			else
				Dependency = null;
			
			if (Dependency)
			{
				for (Second_Fill_Index in Fills)
				{
					if (Fills[Second_Fill_Index]["Namespace"] == Dependency["Namespace"])
					{
						var Already = true;
						break;
					}
				}
			}
		}
		
		if (!Already)
		{
//			console.log("FILL: " + Fill_Element["Namespace"]);
			if (Fill_Element["URL"])
			{
//				console.log(Fill_Element["URL"]);
				Fill({"ID": Fill_Element["Namespace"], "URL": Fill_Element["URL"], "Not_From_Last_Path_Item": true});
				Updates += Fill_Element["Namespace"] + " >>> " + Fill_Element["URL"] + "\n\n";
			}
			else
			{
				Fill({"ID": Fill_Element["Namespace"], "URL": Directory + "/" + Last_Path + "/" + "Raw" + ":" + "Namespace" + "=" + Fill_Element["Namespace"] + ",From_Last_Path_Item=1"});
			}
			
			/*
			
				if (Dependency["Template"] != "HTML")
					Fill({"ID": Dependency["Namespace"], "URL": Request_URL + "/" + Dependency["Template"] + "/" + "Raw" + ":" + "Namespace" + "=" + Dependency["Namespace"]});
				else
					Fill({"ID": Dependency["Namespace"], "URL": Request_URL + "/" + "Raw" + ":" + "Namespace" + "=" + Dependency["Namespace"]});
			*/
		}
	}
	
	Fills = new Object();
}

// TODO: Que?
// Register a dependency between elements or between an element and an item.
function Add_Dependency(Dependency)
{
	var Object_Wrapper = $(Dependency["Namespace"]);
	if (Object_Wrapper)
	{
		// Add event handlers
		Object_Wrapper.onmousemove = Handle_Hover;
		Object_Wrapper.oncontextmenu = Handle_Context_Click;
		Object_Wrapper.onclick = Handle_Context_Click;
		Object_Wrapper.className = "Namespace";
	}
	
	// Store dependency by its ID
	if (!Dependencies[Dependency["ID"]])
		Dependencies[Dependency["ID"]] = new Array();
	Dependencies[Dependency["ID"]].push(Dependency);
	
	// Store dependency by its namespace so parent lookups can be easily found
	if (Dependency["Namespace"])
		Dependencies_By_Namespace[Dependency["Namespace"]] = Dependency;
	
	// Fill Last_Path_Items
	if (Dependency["ID"] == "Last_Path_Item" && Directory_Set)
		Fill({"ID": Dependency["Namespace"], "URL": Directory + "/" + Last_Path + "/" + "Raw" + ":" + "Namespace" + "=" + Dependency["Namespace"] + ",From_Last_Path_Item=1"});
}

// Register elements that are dependent on an item in a list of elements to be refreshed.
function Item_Changed(Item_ID)
{
	var Dependency;
//	console.log("Changed: " + Item_ID);
	if (Dependencies[Item_ID])
	{
		for (Item_Index in Dependencies[Item_ID])
		{
			Dependency = Dependencies[Item_ID][Item_Index];
			if (Dependency["Namespace"])
			{
//				console.log("Starting at: " + Dependency["Namespace"]);
				while (Dependency)
				{
					if (Dependency["URL"] || Dependency["Template"])
					{
//						console.log("Fill: " + Dependency["Namespace"]);
						Dependency_Element = $(Dependency["Namespace"]);
						if (Dependency_Element)
							Fills[Dependency["Namespace"]] = Dependency;
//						else
//							console.log("Not found: " + Dependency["Namespace"]);
						Dependency = null;
					}
					else
					{
						if (Dependency["Parent_Namespace"])
							Dependency = Dependencies_By_Namespace[Dependency["Parent_Namespace"]];
						else
							Dependency = null;
					}
				}
			}
		}
	}
}

// Initiate upload using flash.
function Upload_File(File_Type, Input_ID)
{
	var Jelly_Flash_Extras = $("Jelly_Flash_Extras");
	
	if (!Jelly_Flash_Extras)
	{
		Upload_Update("Error", "No uploader present.");
		return;
	}
	
	Jelly_Flash_Extras.Upload_File("/", File_Type, Input_ID);
}

// Periodically continue upload and display status until completed.
function Upload_Update(Update_Type, Update_Description, Bytes_Loaded, Bytes_Total, Result)
{
	switch (Update_Type)
	{
		case "Update":
//			document.title = "update";
			break;
		case "Complete":
//			document.title = "Complete";
			alert(Result);
			eval(Result);
//			HideWindow("Loading");
			break;
		case "Progress":
//			document.title = "progress";
//			if (Bytes_Loaded == Bytes_Total)
//				$("Loading-Text").innerHTML = "Uploading (please wait)" + "<div style=\"width: 202px; height: 1px;\">&nbsp;</div><div style=\"position: absolute; z-index: 200; width: 200px; height: 16px; border-style: solid; border-width: 1px; border-color: #FC7C00; background-color: white;\"><div style=\"overflow: hidden; width: " +  Math.floor((Bytes_Loaded / Bytes_Total) * 200) + "px; height: 16px; background-color: #FC7C00;\">&nbsp;</div></div>";
//			else
//				$("").style.width = Bytes_Loaded / Bytes_Total * 150;
//				$("Loading-Text").innerHTML = "Uploading (" + Math.floor((Bytes_Loaded / Bytes_Total) * 100) + "%)" + "<div style=\"width: 202px; height: 1px;\">&nbsp;</div><div style=\"position: absolute; z-index: 200; width: 200px; height: 16px; border-style: solid; border-width: 1px; border-color: #FC7C00; background-color: white;\"><div style=\"overflow: hidden; width: " +  Math.floor((Bytes_Loaded / Bytes_Total) * 200) + "px; height: 16px; background-color: #FC7C00;\">&nbsp;</div></div>";
			break;
		case "Error":
			alert("Error: " + Update_Description);
//			HideWindow("Loading");
			break;
	}
}