function dump(arr,level) {
	var dumped_text = "";
	if(!level) level = 0;
	
	//The padding given at the beginning of the line.
	var level_padding = "";
	for(var j=0;j<level+1;j++) level_padding += "    ";
	
	if(typeof(arr) == 'object') { //Array/Hashes/Objects 
		for(var item in arr) {
			var value = arr[item];
			
			if(typeof(value) == 'object') { //If it is an array,
				dumped_text += level_padding + "'" + item + "' ...\n";
				dumped_text += dump(value,level+1);
			} else {
				dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
			}
		}
	} else { //Stings/Chars/Numbers etc.
		dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
	}
	return dumped_text;
}

// JSON parser from http://www.JSON.org/json2.js
function jsonParse(text, reviver)
{
	var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
        
	// The parse method takes a text and an optional reviver function, and returns
	// a JavaScript value if the text is a valid JSON text.
	
	var j;
	
	var walk = function(holder, key)
	{
		// The walk method is used to recursively walk the resulting structure so
		// that modifications can be made.
		
		var k, v, value = holder[key];
		if (value && typeof value === 'object')
		{
			for (k in value)
			{
				if (Object.hasOwnProperty.call(value, k))
				{
					v = walk(value, k);
					if (v !== undefined)
						value[k] = v;
					else
						delete value[k];
				}
			}
		}
		return reviver.call(holder, key, value);
	}
	
	
	// Parsing happens in four stages. In the first stage, we replace certain
	// Unicode characters with escape sequences. JavaScript handles many characters
	// incorrectly, either silently deleting them, or treating them as line endings.
	
	cx.lastIndex = 0;
	if (cx.test(text))
	{
		text = text.replace(cx, function (a)
		{
			return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
		});
	}
	
	// In the second stage, we run the text against regular expressions that look
	// for non-JSON patterns. We are especially concerned with '()' and 'new'
	// because they can cause invocation, and '=' because it can cause mutation.
	// But just to be safe, we want to reject all unexpected forms.
	
	// We split the second stage into 4 regexp operations in order to work around
	// crippling inefficiencies in IE's and Safari's regexp engines. First we
	// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
	// replace all simple value tokens with ']' characters. Third, we delete all
	// open brackets that follow a colon or comma or that begin the text. Finally,
	// we look to see that the remaining characters are only whitespace or ']' or
	// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

	if (/^[\],:{}\s]*$/.
	test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
	replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
	replace(/(?:^|:|,)(?:\s*\[)+/g, '')))
	{
		// In the third stage we use the eval function to compile the text into a
		// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
		// in JavaScript: it can begin a block or an object literal. We wrap the text
		// in parens to eliminate the ambiguity.
		
		j = eval('(' + text + ')');
		
		// In the optional fourth stage, we recursively walk the new structure, passing
		// each name/value pair to a reviver function for possible transformation.
		
		return typeof reviver === 'function' ? walk({'': j}, '') : j;
	}
	
	// If the text is not JSON parseable, then a SyntaxError is thrown.
	
	throw new SyntaxError('JSON.parse');
}

jQuery(function($)
{
	var getClosestElement = function(filter, element)
	{
		var obj = $(element);
		if(obj.filter(filter).length > 0)
			return obj.eq(0);
			
		else return obj.parents(filter).eq(0);
	};
	
	var getTableEntryConstructor = function(fields)
	{
		
		return function(element)
		{
			var entry = { }
			
			entry.contianer = getClosestElement("table", element);
			entry.row = getClosestElement("tr", element);
			
			// fields
			entry.fields = { };			
			for(i in fields)
			{
				field = fields[i];
				var name = typeof(field) == "string" ? field : field.name;
				if($("td."+name, entry.row).length != 1)
					continue;
				entry.fields[name] = { };
				entry.fields[name]['type'] = field.type ? field.type : 'text';
				entry.fields[name]['edit'] = $("td." + name + " .edit input", entry.row);
				entry.fields[name]['display'] = $("td." + name + " .display", entry.row);
				if(field.getDisplay)
					entry.fields[name]['getDisplay'] = field.getDisplay
				if(field.setDisplay)
					entry.fields[name]['setDisplay'] = field.setDisplay
			}
			
			// collections
			entry.edits = $(".edit", entry.row);
			entry.displays = $(".display", entry.row);
			
			// ajax loader icon
			entry.ajaxLoader = $("IMG.ajax-loader", entry.row);
			
			// buttons
			entry.buttons = $(".buttons", entry.row);
			entry.btnSave = $(".btnSave", entry.row);
			entry.btnCancel = $(".btnCancel", entry.row);
			entry.btnEdit = $(".btnEdit", entry.row);
			entry.btnDelete = $(".btnDelete", entry.row);
			
			return entry;
		};
	};
	
	var getLimitedDisplayValue = function()
	{
		var val = '';
		val += $(".shown", this.display).html();
		val += $(".rest", this.display).html();
		return val;
	};
	var setLimitedDisplayValue = function(val)
	{
		var shown = $(".shown", this.display);
		var rest = $(".rest", this.display);
		if(val.length > 24)
		{
			shown.html(val.substring(0, 21));
			rest.html(val.substring(21, val.length));
			$(".showrest", this.display).show();
		}
		else
		{
			shown.html(val);
			rest.html("");
			$(".showrest", this.display).hide();
		}
	};

	
	var hookTable = function(container, fields, saveCallback, delCallback)
	{
		if(container.length == 0)
			return;
		
		var table = { };
		table.container = container;
		table.newEntryTemplate = $("tr.new-entry-template", table.container);
		table.appendTo = $("tbody", table.container);
		table.entryConstructor = getTableEntryConstructor(fields);
		table.saveCallback = saveCallback;
		table.delCallback = delCallback;
		editable.hook(table);
		
		$("table", container).tablesorter();
	};
	
	var doAjax = function(options)
	{
		var ajaxOptions = $.extend(
		{
			dataType: "json",
			timeout: 10000,
			type: "POST",
			cache: false,
			errorDoDefault: true,
			successDoDefault: true,
			completeDoDefault: true
		}, options);
		
		ajaxOptions.error = function(XMLHttpRequest, textStatus, errorThrown)
		{
			if(ajaxOptions.errorDoDefault)
			{
				alert("An error occured while processing your request, please try again. (" + textStatus + ")\n\nPlease report the following:\n\n" + XMLHttpRequest.responseText);
			}
			if(options.error)
				options.error(XMLHttpRequest, textStatus, errorThrown);
		};
		
		ajaxOptions.success = function(data, textStatus)
		{
			if(data.errors && data.errors.length > 0)
			{
				if(ajaxOptions.errorDoDefault)
				{
					var message = "An error occured while processing your request:\n\n";
					$.each(data.errors, function()
					{
						message = message + this.code + ": " + this.message + "\n";
					});
					alert(message);
					return;
				}
			}
			if(ajaxOptions.successDoDefault)
			{
				
			}
			if(options.success)
				options.success(data, textStatus);
		};
		
		ajaxOptions.complete = function()
		{
			if(ajaxOptions.completeDoDefault)
			{
				
			}
			if(options.success)
				options.complete();
		};
							
		
		$.ajax(ajaxOptions);
		
	};
	
	var getCallbackFunction = function(func, type, options)
	{
		options = $.extend(
		{
			successDoDefault: true,
			completeDoDefault: true
		}, options);
		return function(entry)
		{
			var url = "/ajax/data/" + func + "/" + type;
			var array;
			if(func == "save-account")
				array = $(":input", entry.row).serializeArray();
			else if(func == "del")
				array = $("input:hidden[name='id']", entry.row).serializeArray();
				
			entry.buttons.hide();
			entry.ajaxLoader.show();
			entry.row.addClass("ajax-loading");
			$("*", entry.row).attr("disabled", true);
			
			if($("input[type=file]", entry.row).length > 0)
			{
				// there is a file input, we must use an iframe to submit the form
				
				// make the iframe and hide it
				var iframeName = "uploader" + (new Date()).getTime();
				var iframe = $( "<iframe name=\"" + iframeName + "\" src=\"about:blank\" />" );
				iframe.css("display", "none");
				
				// make the form and attach it
				var form = $("<form method=\"post\" enctype=\"multipart/form-data\"></form>")
				form.attr("action", url);
				form.attr("target", iframeName);
				form.css("display", "none");
				$("body:first").append(form);
				
				// insert hidden fields for each data key
				$.each(array, function()
				{
					var input = $("<input type=\"hidden\" />");
					input.attr("name", this.name);
					input.attr("value", this.value);
					form.append(input);
				});
				
				// Move the real input field to the new form and insert a place-holder cloned input
				// field in place of the old one so the user notice it is now missing.
				var real = $("input[type=file]", entry.row);
				var cloned = real.clone(true);
				real.hide();
				real.removeAttr("disabled");
				cloned.insertAfter(real);
				real.appendTo(form);

				var deleteFlag = false;
				
				// handle the iframe load event
				iframe.load(function(event)
				{
					// check and make sure that the page has loaded (fix to chrome bug)
					if(iframe[0].contentDocument && iframe[0].contentDocument.location == "about:blank")
						return;
					else if(iframe[0].contentWindow && iframe[0].contentWindow.document.location == "about:blank")
						return;

					if(deleteFlag)
					{
						// remove the iframe and form
						setTimeout(function() { iframe.remove(); form.remove(); }, 100);
						return;
					}
										
					// parse json result
					var body = window.frames[iframeName].document.getElementsByTagName("body")[0];
					
					var json = false;
					try
					{
						json = jsonParse($(body).html());
					}
					catch(error)
					{
						// result was not able to be parsed into json
						alert("An error occured while processing your request, please try again. (" + "parsererror" + ")\n\nPlease report the following:\n\n" + $(body).html());
						if(func == "del")
						{
							entry.row.removeClass("highlighted-error");
						}
					}
					
					if(json)
					{
						// the json was parsed successfully 
						if(json.errors && json.errors.length > 0)
						{
							// an error was reported by the server
							var message = "An error occured while processing your request:\n\n";
							$.each(json.errors, function()
							{
								message = message + this.code + ": " + this.message + "\n";
							});
							alert(message);
							if(func == "del")
							{
								entry.row.removeClass("highlighted-error");
							}
						}
						else
						{
							// success
							if(options.successDoDefault)
							{
								if(func == "save-account")
								{
									if(json.insertid)
										$("input:hidden[name='id']", entry.row).val(json.insertid);
										
									editable.actions.save(entry);
									
									for(i in json.saved)
									{
										if(entry.fields[i])
										{
											if(entry.fields[i].setDisplay)
												entry.fields[i].setDisplay(json.saved[i]);
											else
												entry.fields[i].display.text(json.saved[i]);
										}
									}
								}
								else if(func == "del")
								{
									entry.row.fadeOut("normal", function() { entry.row.remove(); } );
									$("*", entry.row).fadeOut("normal");
								}
							}
							if(options.success)
								options.success(entry, json);
						}
					}
					
					// complete
					if(options.completeDoDefault)
					{
						entry.ajaxLoader.hide(); 
						entry.buttons.show(); 
						entry.row.removeClass("ajax-loading"); 
						$("*", entry.row).removeAttr("disabled");
					}
					if(options.complete)
						options.complete(entry);
					
					// Reload blank page, so that reloading main page
					// does not re-submit the post. Also, remember to
					// delete the frame
					deleteFlag = true;
					iframe.attr("src", "about:blank"); //load event fired	
				});
				
				// attach the iframe
				$("body:first").append(iframe);
				
				// submit the form (to the iframe)
				form.submit();
			}
			else
			{
				// no file input means it is safe to use ajax
				doAjax(
				{
					url:		url,
					data:		array,
					error: 		function(XMLHttpRequest, textStatus, errorThrown)
								{
									if(func == "del")
									{
										entry.row.removeClass("highlighted-error");
									}
								},
					success: 	function(data, textStatus)
								{
									if(options.successDoDefault)
									{
										if(func == "save-account")
										{
											if(data.insertid)
												$("input:hidden[name='id']", entry.row).val(data.insertid);
												
											editable.actions.save(entry);
											for(i in data.saved)
											{
												if(entry.fields[i])
												{
													if(entry.fields[i].setDisplay)
														entry.fields[i].setDisplay(data.saved[i]);
													else
														entry.fields[i].display.text(data.saved[i]);
												}
											}
										}
										else if(func == "del")
										{
											entry.row.fadeOut("normal", function() { entry.row.remove(); } );
											$("*", entry.row).fadeOut("normal");
										}
									}
									if(options.success)
										options.success(entry, data, textStatus);
								},
					complete: 	function()
								{
									if(options.completeDoDefault)
									{
										entry.ajaxLoader.hide(); 
										entry.buttons.show(); 
										entry.row.removeClass("ajax-loading"); 
										$("*", entry.row).removeAttr("disabled");
									}
									if(options.complete)
										options.complete(entry);
								}
				});
			}
			
			return false;
		}

	}
	
	// tablesorter default values
	$.tablesorter.defaults.cancelSelection = true;
	$.tablesorter.defaults.cssHeader = "sortable";
	$.tablesorter.defaults.cssDesc = "descending";
	$.tablesorter.defaults.cssAsc = "ascending";
	
	
	
	// account details
	var accountType = $("#account-details .entry-type .edit option:contains(" +$("#account-details .entry-type .display").html()+")").val();
	var accountSaveCompleteCB = function(entry)
	{
		$("*", entry.row).removeAttr("disabled");
		
		// IE7 fix for linebreaks on notes
		if($.browser.msie && $.browser.version.substr(0,1)=="7")
		{
			var notes = entry.fields["notes"].display.text();
			entry.fields["notes"].display.text(notes.replace(/\r?\n/g, "\r"));
		}
		
		// reload the page if the account type changed
		var newType = $("#account-details .entry-type .edit option:contains(" + $("#account-details .entry-type .display").html() + ")").val();
		if(newType != accountType)
			window.location.reload();
		else
		{
			entry.ajaxLoader.hide(); 
			entry.buttons.show(); 
			entry.row.removeClass("ajax-loading"); 
		}
	};
	var accountSaveCBOptions = { completeDoDefault: false, complete: accountSaveCompleteCB };
	editable.hook({ container: $("#account-details"), saveCallback: getCallbackFunction("save-account", "account-details", accountSaveCBOptions) });
	
	
	// email accounts
	var emailFields =
	[
		"operator", "address", "username", "password", "pop_server", "smtp_server",
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-emails"), emailFields, getCallbackFunction("save-account", "email"), getCallbackFunction("del", "email"));
	
	
	// routers
	var routerFields =
	[
		"description", "internal_ip", "wan_ip", "username", "password", "ssid",
		{
			name: "wepwpa",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		},
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-routers"), routerFields, getCallbackFunction("save-account", "router"), getCallbackFunction("del", "router"));
	
	
	// computers	
	var computerFields =
	[
		"description", "username", "password", "netbios", "network", "os", "remote_username", "remote_password",
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-computers"), computerFields, getCallbackFunction("save-account", "computer"), getCallbackFunction("del", "computer"));
	
	
	// printers	
	var printerFields =
	[
		"description", "model", "ip",
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-printers"), printerFields, getCallbackFunction("save-account", "printer"), getCallbackFunction("del", "printer"));
	
	
	// networks	
	var networkFields =
	[
		"description", "ip_mask", "subnet_mask", "gateway",
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-networks"), networkFields, getCallbackFunction("save-account", "network"), getCallbackFunction("del", "network"));
	
	
	// servers	
	var serverFields =
	[
		"description", "name", "ip", "os",
		{
			name: "applications",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		},
		"username", "password", "remote_username", "remote_password",
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-servers"), serverFields, getCallbackFunction("save-account", "server"), getCallbackFunction("del", "server"));
	
	
	// support	
	var supportFields =
	[
		"description", "company", "account_number", "password", "phone",
		{
			name: "incidents",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		},
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	hookTable($("#account-support"), supportFields, getCallbackFunction("save-account", "support"), getCallbackFunction("del", "support"));
	
	
	// attachments	
	var attachmentFields =
	[
		{ name: "name", type: "file" }, 
		"size", "added_date",
		{
			name: "notes",
			getDisplay: getLimitedDisplayValue,
			setDisplay: setLimitedDisplayValue
		}
	];
	attachmentSaveOpts =
	{
		success: function(entry)
		{
			$(".name .edit", entry.row).remove();
			entry.fields["name"].edit = $([]);
		}
	};
	hookTable($("#account-attachments"), attachmentFields, getCallbackFunction("save-account", "attachment", attachmentSaveOpts), getCallbackFunction("del", "attachment"));

	
	// accounts
	$("table#accounts").tablesorter();
	
	// account delete
	$("table#accounts .btnDelete").click(function()
	{
		var row = getClosestElement("tr", this);
		row.addClass("highlighted-error");
		
		if(!confirm("Are you sure you want to permanently delete this account?"))
		{
			row.removeClass("highlighted-error");
			return false;
		}

		$(".buttons", row).hide();
		$(".ajax-loader", row).show();
		row.addClass("ajax-loading");
		
		doAjax(
		{
			url:		"/ajax/data/del/account",
			data: 		$("input:hidden[name='id']", row).serializeArray(),
			error: 		function(XMLHttpRequest, textStatus, errorThrown)
						{
							row.removeClass("highlighted-error");
						},
			success: 	function(data, textStatus)
						{
							row.fadeOut("normal", function() { row.remove(); } );
							$("*", row).fadeOut("normal");
						},
			complete: 	function()
						{
							$(".ajax-loader", row).hide();
							$(".buttons", row).show();
							row.removeClass("ajax-loading"); 
						}
		});
		
		return false;
	});
	
	// account user permissions YES
	$("table#accounts .btnPermissionYes").click(function()
	{
		var row = getClosestElement("tr", this);

		$(".buttons", row).hide();
		$(".ajax-loader", row).show();
		row.addClass("ajax-loading");
		
		doAjax(
		{
			url:		"/ajax/data/user-permission/account/0",
			data: 		$("input:hidden[name='account_id'], input:hidden[name='user_id']", row).serializeArray(),
			success: 	function(data, textStatus)
						{
							$(".btnPermissionYes", row).hide();
							$(".btnPermissionNo", row).show();
							var curCount = parseInt($(".account_permission_count").text());
							$(".account_permission_count").text(curCount-1);
						},
			complete: 	function()
						{
							$(".ajax-loader", row).hide();
							$(".buttons", row).show();
							row.removeClass("ajax-loading"); 
						}
		});
		
		return false;
	});
	
	// account user permissions NO
	$("table#accounts .btnPermissionNo").click(function()
	{
		var row = getClosestElement("tr", this);

		$(".buttons", row).hide();
		$(".ajax-loader", row).show();
		row.addClass("ajax-loading");

		doAjax(
		{
			url:		"/ajax/data/user-permission/account/1",
			data: 		$("input:hidden[name='account_id'], input:hidden[name='user_id']", row).serializeArray(),
			success: 	function(data, textStatus)
						{
							$(".btnPermissionNo", row).hide();
							$(".btnPermissionYes", row).show();
							var curCount = parseInt($(".account_permission_count").text());
							$(".account_permission_count").text(curCount+1);
						},
			complete: 	function()
						{
							$(".ajax-loader", row).hide();
							$(".buttons", row).show();
							row.removeClass("ajax-loading"); 
						}
		});
		
		return false;
	});
	
	
	
	// licenses
	$("table#licenses .btnDelete").click(function()
	{
		var row = getClosestElement("tr", this);
		row.addClass("highlighted-error");
		
		if(!confirm("Are you sure you want to permanently delete this license?"))
		{
			row.removeClass("highlighted-error");
			return false;
		}
		
		$(".buttons", row).hide();
		$(".ajax-loader", row).show();
		row.addClass("ajax-loading");

		doAjax(
		{
			url:		"/ajax/data/del/license",
			data: 		$("input:hidden[name='id']", row).serializeArray(),
			error: 		function(XMLHttpRequest, textStatus, errorThrown)
						{
							row.removeClass("highlighted-error");
						},
			success: 	function(data, textStatus)
						{
							row.fadeOut("normal", function() { row.remove(); } );
							$("*", row).fadeOut("normal");
						},
			complete: 	function()
						{
							$(".ajax-loader", row).hide();
							$(".buttons", row).show();
							row.removeClass("ajax-loading"); 
						}
		});
		
		return false;
	});
	
	$("table#licenses .btnUnassign").click(function()
	{
		var row = getClosestElement("tr", this);
		row.addClass("highlighted-warning");
		
		if(!confirm("Are you sure you want to remove this license assignment?"))
		{
			row.removeClass("highlighted-warning");
			return false;
		}
		
		$(".buttons", row).hide();
		$(".ajax-loader", row).show();
		row.addClass("ajax-loading");
		
		doAjax(
		{
			url:		"/ajax/data/unassign/license",
			data: 		$("input:hidden[name='id']", row).serializeArray(),
			success: 	function(data, textStatus)
						{
							$(".account", row).html("");
							$(".computer", row).html("");
							$(".btnUnassign", row).hide();
							$(".btnAssign", row).show();
						},
			complete: 	function()
						{
							row.removeClass("highlighted-warning");
							$(".ajax-loader", row).hide();
							$(".buttons", row).show();
							row.removeClass("ajax-loading"); 
						}
		});
		
		return false;
	});
	
	$("table#licenses").tablesorter();
	
	
	// users
	$("table#users .btnDelete").click(function()
	{
		var row = getClosestElement("tr", this);
		row.addClass("highlighted-error");
		
		if(!confirm("Are you sure you want to permanently delete this user?"))
		{
			row.removeClass("highlighted-error");
			return false;
		}
		
		$(".buttons", row).hide();
		$(".ajax-loader", row).show();
		row.addClass("ajax-loading");

		doAjax(
		{
			url:		"/ajax/data/del/user",
			data: 		$("input:hidden[name='id']", row).serializeArray(),
			error: 		function(XMLHttpRequest, textStatus, errorThrown)
						{
							row.removeClass("highlighted-error");
						},
			success: 	function(data, textStatus)
						{
							row.fadeOut("normal", function() { row.remove(); } );
							$("*", row).fadeOut("normal");
						},
			complete: 	function()
						{
							$(".ajax-loader", row).hide();
							$(".buttons", row).show();
							row.removeClass("ajax-loading"); 
						}
		});
		
		return false;
	});
	
	$("table#users").tablesorter()
	
});