//Cortex constants
PATH_ROOT = "";
PATH_MODULES = "/modules";
DOMAIN_FULL = "http://www.ukpayments.org.uk";
EXT_PATH_TEMPLATES = "/templates";
EXT_PATH_MODULES = "/modules";
LANG_ID = "en-gb";
DOMAIN_ID = "9";
DEBUG = "";
CTX_RESPONSE_POSITIVE = "1";
CTX_FLASH_IDENTIFIER = "ctx_swf";
POST_TOKEN = "3f721650f5f975bdf5005fe3dc5aa241";
PORT_HTTP = "80";
PORT_HTTPS = "443";
MAX_BASIC_LOGINS = parseInt("3");

//AJAX related variables
AJAX_LOADING_TIMERS = Array(); //timers for current AJAX requests - nothing to do with TIMERS[]
AJAX_LOAD_TIMEOUT = 1000;
AJAX_DATA_TIMEOUT = 10000;
LAST_AJAX_ID = 0;
DISPLAYED_TIPS = Array();
AJAX_QUEUE = Array();

//locale strings from the loaded $LANG array
var LANG = new Array();
LANG['form_buttons'] = new Array();
LANG['user_confirm'] = new Array();
LANG['user_messages'] = new Array();
LANG['user_errors'] = new Array();
LANG['words'] = new Array();

LANG['words']['page_singular'] = "Page";

LANG['form_buttons']['ok'] = "OK";

var LCL_LOADING = "Loading";

//others
var TIMERS = Array(); //stored global timers from setTimeout and setInterval
var RTE_EDITORS = Array(); //editor object array for RTE
var ACTIVE_TCOMBO = null; //combo box object for typeable combos
var IS_IE6 = navigator.appVersion.toString().match(/MSIE\s*6/);
var DOM_CACHE = Array(); //stored deleted items
var DRAG_OBJ = null; //dragging divs object
var MANUAL_ELEMENTS = Array(); //form elements that should not be altered automatically

var DL_PAGE = 1; //current data list page
var DL_ACTION = null; //(last) data list action
var DL_POSTDATA = null; //last original data list post data
var DL_LIST_TYPE = null; //last listing type (usually "list", sometimes "search"!)
var DL_LIST_NODE = "dynDataList"; //last listing container node (usually "dynDataList")
//pagelist tables
DYNTABLE_SELECTORS = Array(); //column selection dialogs (one per table)

//response timestamps
var XMLR_LAST_TS = 0;


attach_event(window, "load", function() {
	new ctx_shader_display;
});

function shortcut(key, callback, ctrl) {
	this.callback = callback;
	
	this.keycode = key.toUpperCase().charCodeAt(0);
	
	if (this.keycode < 65 || this.keycode > 90) {
		alert("shortcut() shortcut key must be between A and Z");
		return false;
	}
	
	if (ctrl == null) {
		this.ctrl = true;
	} else {
		this.ctrl = ctrl;
	}
	
	attach_event(document, "keydown", obj_event(this, "exec_shortcut"));
}

shortcut.prototype.exec_shortcut = function(e) {
	if (e.which) {
		code = e.which;
	} else {
		code = e.keyCode;
	}
	
	if (code == this.keycode && 
		((e.ctrlKey == true || e.metaKey == true) && this.ctrl == true)) { 
		//run code for CTRL+S -- ie, save!
		try {
			e.preventDefault();
		} catch(err) {
			e.returnValue = false;
		}
		
		if (window.event) {
			window.event.cancelBubble = true;
		} else {
			if (e != null)
				e.stopPropagation();
		}
		
		this.callback();
		
		return false;
	}
}

function clear_user_message() {
	//clears the user message box
	var message_box = $("messageBox");
	message_box.innerHTML = "&nbsp;";	
}

function display_tip(tip_name, tip_text, x, y) {
	//display the tip in a panel	
	if ($("tipPanel")) {
		destroy_element("tipPanel");
	}
	
	panel = document.createElement("DIV");
	panel.id = "tipPanel";
	panel.rel = tip_name;
	
	panel_top = document.createElement("DIV");
	panel_top.id = "tipPanelTop";
	
	panel_top.innerHTML = tip_text;
	
	//store in global
	DISPLAYED_TIPS[tip_name] = tip_text;
	
	panel_bottom = document.createElement("DIV");
	panel_bottom.id = "tipPanelBottom";
	
	document.body.appendChild(panel);
	
	panel.style.left = x - (panel.offsetWidth / 2) + "px";
	panel.style.top = y + 10 + "px";
	
	panel.appendChild(panel_top);
	panel.appendChild(panel_bottom);
	
	ie6_top_objects(true);
	slidein_element(panel, "block");
	
	attach_event(panel, "click", function() {
		ie6_top_objects(false);
		destroy_element("tipPanel", true);
		if (TIMERS['display_tip'] != null) {
			window.clearTimeout(TIMERS['display_tip']);
		}
	});
	
	//auto destroy the tip after 6 seconds
	TIMERS['display_tip'] = window.setTimeout(
		function() { destroy_element("tipPanel", true); }, 
		15000
	);
}

function panel_select(panel_id, rte_id) {
	//display the selected panel (panel_id) and highlight the button (e.source)
	var tool_panel_id = rte_id + "_toolpanel_" + panel_id;
	var tool_button_id = rte_id + "toolbutton_" + panel_id;
	
	panel = $(tool_panel_id);
		
	if (panel != null) {
		//hide old panel
		if (ACTIVE_TOOL_PANEL != null) {
			var active_panel = rte_id + "_toolpanel_" + ACTIVE_TOOL_PANEL;
			
			if ($(active_panel) != null) {
				element_display($(active_panel), "none");
				var active_button = rte_id + "_toolbutton" + ACTIVE_TOOL_PANEL;
				
				if ($(active_button) != null)
					$(active_button).className = "";
			}
		}
		
		//display new one
		element_display(panel, "block");
		
		//highlight button
		if ($(tool_button_id) != null)
			$(tool_button_id).className = "active";
		
		ACTIVE_TOOL_PANEL = panel_id;
		
		if (ACTIVE_TOOL_PANEL == panel_id) {
			//run inserting code for specific panels if it isn't already being displayed
			switch (panel_id) {
				case "insert_image":
					filter = String("image/gif,image/jpeg,image/png").urlencode();
					get_data_list(null, null,
						PATH_ROOT + "/admin/files/actions.php", 1,
						"rte_id=" + rte_id + "&filter=" + 
							filter + 
							"&select_type=image",
						"list_insert",
						"dynDataList");
					break;
					
				case "insert_flash":
					filter = String("application/x-shockwave-flash").urlencode();
					get_data_list(null, null,
						PATH_ROOT + "/admin/files/actions.php", 1,
						"rte_id=" + rte_id + "&filter=" + 
							filter + 
							"&select_type=flash",
						"list_insert",
						"dynDataListFlash");
					break;
					
				case "insert_plugin":
					filter = String("application/x-shockwave-flash").urlencode();
					get_data_list(null, null,
						PATH_ROOT + "/admin/page_lists/plugins/actions.php", 1,
						"rte_id=" + rte_id,
						"list_insert",
						"dynDataListPlugins");
					break;
				
				case "insert_internal_link":
					//get default category
					var cat_select = $("rtelink_cat_id");
					var cat_id = cat_select.options[cat_select.selectedIndex].value;
					
					get_data_list(null, null, 
						PATH_ROOT + "/admin/pages/actions.php", 1, 
						"rte_id=" + rte_id + "&link_cat_id=" + cat_id, 
						"list",
						"dynDataListPages");
					break;
					
				case "insert_file_link":
					//file links
					get_data_list(null, null,
						PATH_ROOT + "/admin/files/actions.php", 1,
						"rte_id=" + rte_id + "&filter=null&select_type=link",
						"list_insert",
						"dynDataListFiles");	
					break;		
				
				case "rte_source":
					if (rte_id != null) {
						RTE_EDITORS[rte_id].get_source();
					}
			}
		}
	} else {
		alert("panel_select(): panel with ID '" + tool_panel_id + "' not found.");
	}
	
	return false;
}

function reset_domain_select() {
	var selector = $("sel_domain_select");
	
	if (selector != null) {
		for (a = 0; a < selector.options.length; a++) {
			if (selector.options[a].value == DOMAIN_ID) {
				selector.options[a].selected = true;
			} else {
				selector.options[a].selected = false;
			}
		} 
	}
	
	return false;
}

function ctx_confirm(message, buttons) {
	
	var a, shell, form, button_shell, focus_obj;
	var abs_container = $("abs_container");
	var root_node = create_element("abs_container", "DIV", "int_dialog", "absObjectInner");
	
	root_node.style.top = scroll_top() + "px";

	this.shell = document.createElement("DIV");
	this.shell.id = "intConfirmShell";
	this.shell.className = "intPopup";
	
	form = new this.create_form(this, message, buttons);
	this.shell.appendChild(form);
	
	this.button_shell = document.createElement("DIV");
	this.button_shell.className = "buttons";
	
	form.appendChild(this.button_shell);

	for (a = 0; a < buttons.length; a++) {
		button = new this.create_button(this, buttons[a]);
		this.button_shell.appendChild(button);
		
		if (buttons[a][2] == true) focus_obj = button;
	}
	
	//create confirmation
	root_node.appendChild(this.shell);
	
	//position confirmation
	root_node.style.top = (scroll_top() + 10) + "px";
	
	if (this.shell.offsetLeft == 0)
		root_node.style.left = ((abs_container.offsetWidth / 2) - (this.shell.offsetWidth / 2)) + "px";
	
	//set focus
	if (focus_obj != null)
		focus_obj.focus();
	
	//shade and hide selects
	ie6_top_objects(true);
	ctx_shade();
}

ctx_confirm.prototype.create_form = function(parent_obj, message, button_arr) {
	this.form = document.createElement("FORM");
	this.form.name = "intconfirmform";
	this.form.id = "intconfirmform";
	this.form.className = "main";
	
	//locate "default" button and add the callback to the parent form's onsubmit 
	for (a = 0; a < button_arr.length; a++) {
		if (button_arr[a][2] == true) {
			//button is a default button, insert the button click event on the form instead				
			if (button_arr[a][1] != null) {
				this.form_submit = function() {
						ie6_top_objects(false);
						destroy_element("int_dialog");
						ctx_unshade();
						button_arr[a][1]();
						return false;
					}
			} else {
				this.form_submit = function() {
						ie6_top_objects(false);
						destroy_element("int_dialog");
						ctx_unshade();
						return false;
					}
			}
		
			attach_event(this.form, "submit", 
				obj_event(parent_obj, "callback_exec", this.form_submit, true));
			break;
		}
	}
	
	message = message.replace(/\n/g, "<br>");
	this.form.innerHTML = "<p>" + message + "</p>";
	
	return this.form;
}

ctx_confirm.prototype.create_button = function(parent_obj, button_arr) {
	this.button = document.createElement("INPUT");
	//this.button.className = "confirmButton";
	
	if (button_arr[2] == true) {
		//the default button doesn't need an onclick as its event is within the form onsubmit
		this.button.type = "submit";
	} else {
		this.button.type = "button";
		
		if (button_arr[1] != null) {
			//perform the button action and unshade
			this.callback = function() {
					ie6_top_objects(false);
					destroy_element("int_dialog");
					ctx_unshade();
					button_arr[1]();
				}
		} else {
			//do nothing (except close the form and unshade)
			this.callback = function() {
					ie6_top_objects(false);
					destroy_element("int_dialog");
					ctx_unshade();
				}
		}	
		
		attach_event(this.button, "click", obj_event(parent_obj, "callback_exec", this.callback));
	}

	//value is set after type, as IE likes to re-set values after type is set
	this.button.value = button_arr[0];
	
	return this.button;
}

ctx_confirm.prototype.callback_exec = function(e, event_obj, click_function) {
	click_function();
}

function ctx_alert(message, button_arr) {
	
	var a, shell, form, button_shell, button, focus_obj;
	var abs_container = $("abs_container");
	var root_node = create_element("abs_container", "DIV", "int_dialog", "absObjectInner");
	
	this.shell = document.createElement("DIV");
	//this.shell.id = "intConfirmShell";
	this.shell.className = "intPopup";
	
	this.form = document.createElement("FORM");
	this.form.name = "intconfirmform";
	this.form.id = "intconfirmform";
	this.form.className = "main";
	
	if (message == null)
		message = new String();
	
	message = message.replace(/\n/g, "<br>");
	this.form.innerHTML = "<p>" + message + "</p>";
	
	this.shell.appendChild(this.form);
	
	this.button_shell = document.createElement("DIV");
	this.button_shell.className = "buttons";
	
	this.form.appendChild(this.button_shell);

	if (button_arr != null) {
		for (a = 0; a < button_arr.length; a++) {
			button = new this.button(
				this,
				button_arr[a]['onclick'],
				button_arr[a]['value']
			);	
	
			this.button_shell.appendChild(button);
		
			if (button_arr[a]['focus'] != null)
				focus_obj = button;
		}
	} else {
		//just create an OK button
		var focus_obj = new this.button(this, null, LANG['form_buttons']['ok']);		
		this.button_shell.appendChild(focus_obj);
	}
	
	//create alert
	root_node.appendChild(this.shell);
	
	//position alert
	root_node.style.top = (scroll_top() + 10) + "px";
	
	if (this.shell.offsetLeft == 0)
		root_node.style.left = ((abs_container.offsetWidth / 2) - (this.shell.offsetWidth / 2)) + "px";
		
	//set focus
	if (focus_obj != null)
		focus_obj.focus();
	
	//shade and hide selects
	ie6_top_objects(true);
	ctx_shade();
}

ctx_alert.prototype.button = function(parent, click_function, value) {
	//create button
	this.button = document.createElement("INPUT");
	//this.button.className = "confirmButton";
	
	this.button.type = "button";
	
	if (click_function != null) {
		click_function = new Function(click_function);
		
		this.callback = function() {
			ie6_top_objects(false);
			destroy_element("int_dialog");
			ctx_unshade();
			click_function();
		}
	} else {
		this.callback = function() {
			ie6_top_objects(false);
			destroy_element("int_dialog");
			ctx_unshade();
		}	
	}
	
	attach_event(this.button, "click", obj_event(parent, "callback_exec", this.callback));

	//value is set after type, as IE likes to re-set values after type is set
	this.button.value = value;
	
	return this.button;
}

ctx_alert.prototype.callback_exec = function(e, event_obj, click_function) {
	click_function();
}

function type_combo_init(parent_node, data, callback, type) {
	
	if (parent_node == null || typeof(parent_node) == "string")
		parent_node = document.body;
	
	var elements = parent_node.getElementsByTagName("input");
	
	for (var a = 0; a < elements.length; a++) {
		//locate input tags with typeCombo as their classname
		if (class_exists(elements[a], "typeCombo")
			&& elements[a].id != ""
			&& $("combo_datalist_" + elements[a].id) == null)
			new type_combo(elements[a], data, callback, type);
	}
}

function type_combo(element, data, callback, type) {
	
	this.element = element;
	this.active_item = null;
	this.data = null;
	this.max_height = 3;
	
	element.setAttribute("autocomplete", "off");
	
	if (type == null) type = "click";
	this.type = type;
	
	var id = "combo_datalist_" + element.id;
	var container = $(id);
	
	if (container == null) {
		this.container = document.createElement("div");
		this.container.id = id;
		this.container.className = "typeComboList";
		this.container.style.width = "250px";
		this.container.style.height = "150px";
	} else {
		this.container = container;
	}
	
	this.update_data(data);
	
	document.body.appendChild(this.container);
	
	if (this.type == "click") {
		attach_event(this.element, "click", obj_event(this, "show", null));
		attach_event(this.element, "keyup", obj_event(this, "hide", null));
	} else {
		attach_event(this.element, "click", obj_event(this, "show", null));
		attach_event(this.element, "keyup", obj_event(this, "show", true));
	}
	
	attach_event(document.body, "click", obj_event(this, "hide_active", null));
	attach_event(window, "resize", obj_event(this, "hide_active", null));
	
	return this;
}

type_combo.prototype.update_data = function(data) {
	
	var item;
	this.data = data;
	this.items = new Array();
	this.highlight_regex = new RegExp("(.*?)(" + this.element.value + ")(.*?)", "gi");
	
	if (data.length <= 0) {
		//no data - disable combo field
		add_class(this.element, "typeComboDisabled");
		destroy_element_children(this.container);
		return false;
	} else {
		//make sure disabled class is removed
		remove_class(this.element, "typeComboDisabled");
	}
	
	destroy_element_children(this.container);
	
	for (var a = 0; a < this.data.length; a++) {
		if (data[a]['value'] != null) {
			item = new this.data_item(this, data[a], a);
			this.container.appendChild(item);
			this.items.push(item);
		}
	}
	
	if (this.container.style.display == "block") {
		//update container height
		var container_height = 0;
		
		for (var a = 0; a < this.items.length; a++) {
			container_height += this.items[a].offsetHeight;
			if (a == this.max_height) break;
		}
		
		this.container.style.height = container_height + "px";
	}
}

type_combo.prototype.hide_active = function(e, event_obj, args) {
	if (ACTIVE_TCOMBO != null)
		ACTIVE_TCOMBO.hide();
	
	if (window.event) {
		window.event.cancelBubble = true;
	} else if (e != null) {
		e.stopPropagation();
	}
}

type_combo.prototype.show = function(e, event_obj, always_show) {
	
	
	//don't show if there's no data
	if (this.data.length <= 0)
		return false;
	
	//hide other combos
	if (ACTIVE_TCOMBO != null && ACTIVE_TCOMBO != this)
		ACTIVE_TCOMBO.hide();
			
	if (this.container.style.display == "none" || this.container.style.display == "") {
		//container being shown
		if (IS_IE6) {
			//i.e. likes its padding, what can i say
			offset_pad = 1
		} else {
			offset_pad = 0;
		}
		
		//set left, top and width
		this.container.style.left = (element_left_pos(this.element) + offset_pad) + "px";
		this.container.style.top = 
			(element_top_pos(this.element) + this.element.offsetHeight + offset_pad) + "px";

		
		this.container.style.width = (this.element.offsetWidth - 2) + "px";
		
		//activate textbox control
		switch_class(this.element, "typeCombo", "typeComboActive");
		
		this.container.style.display = "block";
		this.container.style.visibility = "hidden";
		
		//get container height (up to this.max_height)
		var container_height = 0;
		
		for (var a = 0; a < this.items.length; a++) {
			container_height += this.items[a].offsetHeight;
			if (a == this.max_height) break;
		}
		
		//set height for the container
		this.container.style.height = container_height + "px";
		
		slidein_element(this.container, "block");
		
		ACTIVE_TCOMBO = this;
	} else {
		//container being hidden (or not)
		if (always_show != true) {
			//toggle container
			slideout_element(this.container);
			//this.container.style.display = "none";
			switch_class(this.element, "typeComboActive", "typeCombo");
		}
	}
	
	if (window.event) {
		window.event.cancelBubble = true;
	} else if (e != null) {
		e.stopPropagation();
	}
}

type_combo.prototype.hide = function(e, event_obj, args) {
	
	if (this.container.style.display != "none" && this.container.style.display != "") {
		slideout_element(this.container);
		//this.container.style.display = "none";
		switch_class(this.element, "typeComboActive", "typeCombo");
		ACTIVE_TCOMBO = null;
	}
}

type_combo.prototype.data_item = function(obj, data, index) {
	
	this.value = data['value'];
	this.title = data['title'];
	
	//highlight title if type is key
	if (obj.type == "key" && obj.element.value.trim() != "")
		this.title = this.title.replace(obj.highlight_regex, "$1<b>$2</b>$3");
	
	this.item = document.createElement("div");
	this.item.id = obj.container.id + "_" + index;
	
	if (index % 2 == 0) {
		this.item.className = "typeComboItem alt";
	} else {
		this.item.className = "typeComboItem";
	}
	
	var item_label = document.createElement("h4");
	item_label.innerHTML = this.title;
	
	this.item.appendChild(item_label);
	
	if (data['desc'] != null) {
		var item_desc = document.createElement("span");
		item_desc.className = "typeComboItemDesc";
		item_desc.innerHTML = data['desc'];
		this.item.appendChild(item_desc);
	}
	
	attach_event(this.item, "mouseover", obj_event(obj, "hover", this));
	attach_event(this.item, "click", obj_event(obj, "insert", this));
	
	return this.item;
}

type_combo.prototype.hover = function(e, event_obj, data_item) {
	if (this.active_item != null)
		remove_class(this.active_item.item, "active");
		
	add_class(data_item.item, "active");
	
	this.active_item = data_item;
}

type_combo.prototype.insert = function(e, event_obj, data_item) {
	this.element.value = data_item.value;
	this.hide();
}

function init_file_selector(select) {
	//creates or re-uses a file selectior interstitial object
	if (FILE_SELECTORS[select] == null) {
		new file_selector(select);
	} else {
		FILE_SELECTORS[select].load_file_selector(null, null, select);
	}
}

function file_selector(select) {
	//object for a file selection interstitial
	this.callback_select = $(select);
	
	if (this.callback_select == null) {
		alert("No callback select item with ID '" + select + "' found!");
		return false;
	}
	
	this.selected_insert_element = null;
	
	FILE_SELECTORS[this.callback_select.id] = this;
	
	this.load_file_selector(null, null, this.callback_select.id);
}

file_selector.prototype.load_file_selector = function(xmldoc, response, select_id) {
	
	if (xmldoc != null) {
		select_id = xmldoc.getElementsByTagName("select_id")[0].childNodes[0].nodeValue;
		obj = FILE_SELECTORS[select_id];
		
		get_data_list(null, null,
			PATH_ROOT + "/admin/files/actions.php", 1,
			"rte_id=null&filter=null&select_type=select_link&select_id=" + obj.callback_select.id,
			"list_insert",
			"dynDataListFiles");
	} else {
		post_data = "&a=show_browser&select_id=" + select_id;
		ajax_send(FILE_SELECTORS[this.callback_select.id].load_file_selector,
			arguments, PATH_ROOT + "/admin/files/actions.php", post_data);
	}
}

file_selector.prototype.insert_select = function(element, file_id, name) {
	//store file choice and highlight element
	this.insert_args = Array();
	
	this.insert_args['id'] = file_id;
	this.insert_args['filename'] = name
	
	//unselect the old element
	if (this.selected_insert_element != null)
		remove_class(this.selected_insert_element, "selectHighlight");
	
	//select the element
	add_class(element, "selectHighlight");
	
	this.selected_insert_element = element;
}

file_selector.prototype.insert_item = function() {
	
	destroy_element('file_browser');
	ctx_unshade();
	
	if (this.insert_args != null) {
		//insert item into the select object
		add_select_value(
			this.callback_select,
			this.insert_args['filename'],
			this.insert_args['id'],
			1,
			true
		);
	}
}

function init_page_selector(select) {
	//creates or re-uses a category page selectior interstitial object
	if (PAGE_SELECTORS[select] == null) {
		new page_selector(select);
	} else {
		PAGE_SELECTORS[select].load_page_selector(null, null, select);
	}
}

function page_selector(select) {
	//object for a file selection interstitial
	this.callback_select = $(select);
	
	if (this.callback_select == null) {
		alert("page_selector() - No callback select item with ID '" + select + "' found!");
		return false;
	}
	
	this.selected_insert_element = null;
	
	PAGE_SELECTORS[this.callback_select.id] = this;
	
	this.load_page_selector(null, null, this.callback_select.id);
}

page_selector.prototype.load_page_selector = function(xmldoc, response, select_id) {
	
	if (xmldoc != null) {
		select_id = xmldoc.getElementsByTagName("select_id")[0].childNodes[0].nodeValue;
		obj = PAGE_SELECTORS[select_id];
		
		get_data_list(null, null,
			PATH_ROOT + "/admin/pages/actions.php", 1,
			"select_id=" + obj.callback_select.id,
			"list",
			"dynDataListPages");
	} else {
		post_data = "&a=show_browser&select_id=" + select_id;
		ajax_send(PAGE_SELECTORS[this.callback_select.id].load_page_selector,
			arguments, PATH_ROOT + "/admin/pages/actions.php", post_data);
	}
}

page_selector.prototype.link_cat_update = function(cat_select) {
	//switch page category
	var cat_id = cat_select.options[cat_select.selectedIndex].value;
	var post_data = "cat_id=" + cat_id + "&select_id=" + this.callback_select.id;
	get_data_list(null, null, PATH_ROOT + "/admin/pages/actions.php", 1, post_data);
}

page_selector.prototype.insert_select = function(element, page_id, page_title) {
	//store file choice and highlight element
	this.insert_args = Array();
	
	this.insert_args['id'] = page_id;
	this.insert_args['title'] = page_title;
	
	//unselect the old element
	if (this.selected_insert_element != null)
		remove_class(this.selected_insert_element, "selectHighlight");
	
	//select the element
	add_class(element, "selectHighlight");
	
	this.selected_insert_element = element;
}

page_selector.prototype.insert_item = function() {
	
	destroy_element('page_browser');
	ctx_unshade();
	
	if (this.insert_args != null) {
		//insert item into the select object
		add_select_value(
			this.callback_select,
			this.insert_args['title'],
			this.insert_args['id'],
			1,
			true
		);
	}
}

function listitem_add(xmldoc, response, table_name, item_id, group_id, input_name) {
	
	item_id = item_id.trim();
	
	if (item_id != "" || item_id != 0) {
		//send xml request for list item
		var post_data = "a=load_listitem&id=" + item_id + "&table=" + table_name +
			"&group_id=" + group_id + "&input_name=" + input_name;
			
		ajax_send(null, arguments, PATH_ROOT + "/admin/actions.php", post_data);
	} else {
		new ctx_alert(LANG['user_errors']['must_select_an_item']);
	}
}

function ctx_shader_display() {
	
	var shader_def;
	
	//get all anchors
	var value_reg = new RegExp("^shader(:[^$]*)?", "i");
	this.anchors = $a("a", "rel", value_reg);
	
	this.shader_items = Array();
	
	if (this.anchors.length > 0) {
		for (var a = 0; a < this.anchors.length; a++) {
			shader_def = this.anchors[a].getAttribute("rel").split(":");
			this.shader_items[a] = Array();
			
			if (shader_def[1] != undefined)
				this.shader_items[a]['file_id'] = shader_def[1];
			
			this.shader_items[a]['url'] = this.anchors[a].href;
			this.shader_items[a]['title'] = this.anchors[a].title;
			
			attach_event(
					this.anchors[a],
					"click", 
					obj_event(this, "display", a, true)
				);
		}
	}
}

ctx_shader_display.prototype.display = function(event, event_obj, item_num) {
	
	var anchor = this.shader_items[item_num];
	
	var post_data = "a=shader_panel&uri=" + anchor['url'].urlencode();

	if (anchor['title'] != null)
		post_data += "&caption=" + anchor['title'];

	if (anchor['file_id'] != undefined)
		post_data += "&file_id=" + anchor['file_id'];
		
	ajax_send(obj_event(this, "adjust"), arguments, PATH_ROOT + "/actions.php", post_data);
}

ctx_shader_display.prototype.adjust = function(xmldoc) {
	
	this.shader_dialog = $("shader_display");
	
	if (this.shader_dialog != null) {
		this.close_button = $("a_shader_close");
		
		if (this.close_button != null)
			attach_event(this.close_button, "click", obj_event(this, "remove", null, true));
	}
}

ctx_shader_display.prototype.remove = function() {
	destroy_element(this.shader_dialog);
	ctx_unshade();
	return false;
}

function ctx_shade() {
	//show the shader - a div to make the screen get darker
	var shader
	
	if ($("shader")) {
		shader = $("shader");
		
		if (shader.style.display == "block");
			return false;
	} else {
		shader = document.createElement("div");
		shader.id = "shader";
		document.body.appendChild(shader);
	}
	
	shader.className = "on";
	
	reveal_element(shader, "block", 2, 50);
	
	attach_event(window, "resize", function() { max_size_obj(shader); });
	max_size_obj(shader);
}

function ctx_unshade(force) {
	//remove the shader
	var shader = $("shader");
	var abs_container = $("abs_container");
	
	if (abs_container.childNodes.length > 0 && !force) {
		//look through items to check display/visibility state
		var visible_nodes = false;
		
		for (var a = 0; a < abs_container.childNodes.length; a++) {
			if (abs_container.childNodes[a].nodeType != Node.ELEMENT_NODE)
				continue;
			
			if (abs_container.childNodes[a].style.display != "none"
				&& abs_container.childNodes[a].style.visibility != "hidden")
				visible_nodes = true;
		}
		
		if (visible_nodes)
			//if visible child nodes have been found, do not unshade
			return false;
	}
	
	if (shader != null) {
		detach_event(window, "resize", function() { max_size_obj(shader); });
		//fade_element(shader, 1);
		destroy_element("shader", true);
	}
}

function ctx_filename(filename) {
	
	filename = filename.toLowerCase();
	filename = filename.replace(/\s/g, "_");
	filename = filename.replace(/[\/\\!:*?<>|"]/g, "");
	
	return filename;
}

function download_file(file_id) {
	
	location.href = PATH_ROOT + "/modules/cortex/fetch_file.php?id=" + 
		file_id + "&download=1";
}

function create_file_type() {
	//activates/deactivates the image resizing form if the file is a recognised image
	var img_types = new Array("jpg", "jpeg", "gif", "png");
	var filename = $("file_upload_file").value;
	
	filename = filename.split(".");
	var ext = filename[filename.length - 1];
	var panel = $("div_img_resize_panel");
	
	if (in_array(ext, img_types)) {
		remove_class(panel, "hidden");
	} else {
		add_class(panel, "hidden");
	}
}

function create_file_sizeopt() {
	//activates/deactivates the custom size form if "Custom" is chosen on the size list
	var select = $("sel_img_resize");
	var panel = $("div_img_customsize");
	var panel_ratio = $("div_img_ratio");
	
	var size_val = select.options[select.selectedIndex].value;
	
	if (size_val == "0") {
		remove_class(panel, "hidden");
		remove_class(panel_ratio, "hidden");
	} else if (size_val != "") {
		add_class(panel, "hidden");
		remove_class(panel_ratio, "hidden");
	} else {
		add_class(panel, "hidden");
		add_class(panel_ratio, "hidden");
	}
}

function page_redirect(uri, wait) {
	//redirects to another page after [wait] in seconds, or immediately 
	//if [wait] is specified as 0
	if (parseInt(wait) == 0) {
		window.top.location.href = uri;
	} else {
		window.setTimeout("page_redirect('" + uri + "', 0)", (parseInt(wait) * 1000));
	}
}

function get(var_name) {
	
	var_name = var_name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");

	var regex = new RegExp(var_name + "=([^&#]*)");
	var results = regex.exec(location.search);
	
	if (results == null) {
		return "";
	} else {
		return results[1];
	}
}

function in_array(needle, haystack_arr) {
	//searches haystack_arr Array() for needle string
	for (var a = 0; a < haystack_arr.length; a++) {
		if (haystack_arr[a] == needle) {
			return true;
		}	
	}
	
	return false;
}

function array_shift_byval(value, array) {
	//removes an index with the matching value from the specified array
	var new_array = new Array();
	
	for (var a = 0; a < array.length; a++) {
		if (array[a] != value) {
			new_array[(new_array.length)] = array[a];
		}
	}
	
	return new_array;
}

function array_shift_bykey(remove_key, array) {
	//removes a key from the specified array
	var new_array = new Array();
	
	for (key in array) {
		if (key != remove_key) {
			new_array[key] = array[key];
		}
	}
	
	return new_array;
}

function focus_login_fields() {
	//gives focus to the correct login field on the login page, or when the login
	//interstitial appears
	if (document.forms['loginform'].elements['u'].value != "") {
		document.forms['loginform'].elements['p'].focus()
	} else {
		document.forms['loginform'].elements['u'].focus()	
	}	
}

function update_tpl_key(name_field, tpl_key_field) {
	
	if (tpl_key_field.id == null) {
		alert("update_tpl_key(): Template key field must have an ID");
		return false;
	}
	
	if (MANUAL_ELEMENTS[tpl_key_field.id] != null)
		return false;
		
	var name = name_field.value;
	
	//process name to be template friendly
	name = name.ctx_tpl_name();
	
	tpl_key_field.value = name;
}

function set_manual_element(form_element) {
	
	if (form_element.id == null) {
		alert("set_manual_element(): Form element must have an ID");
		return false;
	}
	
	if (MANUAL_ELEMENTS[form_element.id] == null) {
		MANUAL_ELEMENTS[form_element.id] = true;
	}
}

function set_formvar(item, val) {
	
	var form = document.forms['frm_postdata'];
	
	if (!form) {
		alert("Form frm_postdata does not exist.");
		return false;
	}
	
	if (!form.elements[item]) {
		var input = create_element("frm_postdata", "input", "", "");
		input.name = item;
		input.type = "input";
		input.value = val;
	} else {
		form.elements[item].value = val;
	}
}

function post_formvars(action, method) {
	
	var form = document.forms['frm_postdata'];
	
	if (!form) {
		alert("Form frm_postdata does not exist.");
		return false;
	} else {
		form.action = action;
		
		if (method == "" || method == null)
			method = "POST"
		
		form.method = method;		
		form.submit()
	}
}

function reset_info_bar() {
	
	var infobar = $("div_info_bar");
	
	if (infobar == null)
		return false;
	
	var listitems = $a("tr", "id", new RegExp("listitem_.*"), $("dynDataList"));
	var listitems2 = $a("div", "id", new RegExp("listitem_.*"), $("dynDataList"));
	
	var list_id_part = "listitem_";
	var checkbox_count = 0;
	
	DATALIST_COUNT = 0;
	DATALIST_CHK_COUNT = 0;
	
	for (a = 0; a < listitems.length; a++) {
		if (listitems[a].id.substring(0, list_id_part.length) == list_id_part)
			DATALIST_COUNT++;
		if (listitems[a].getElementsByTagName("input").length > 0)
			checkbox_count++;
	}
	
	for (a = 0; a < listitems2.length; a++) {
		if (listitems2[a].id.substring(0, list_id_part.length) == list_id_part)
			DATALIST_COUNT++;
		if (listitems2[a].getElementsByTagName("input").length > 0)
			checkbox_count++;
	}
	
	if (DATALIST_CHK_COUNT <= 0) {
		var html = DATALIST_COUNT + "&nbsp;" + LCL_ITEMS + "&nbsp;" + LCL_LISTED;
			
		if (checkbox_count > 0) {
			html += '&nbsp;|&nbsp;<a href="#" onclick="return toggle_all_chk(true)">' +
				LCL_SELECT_ALL + '</a>';
		}
		
		infobar.innerHTML = html;
		infobar.className = "";
	}
}

function init_chk() {
	
	var checkboxes = document.getElementsByTagName("input");
	var a;
	var list_id_part = "chk_listitem_";
	
	for (a = 0; a < checkboxes.length; a++) {
		if (checkboxes[a].id.substring(0, list_id_part.length) == list_id_part)
			checkboxes[a].checked = false;
	}
	
	info_bar = $("div_info_bar");
	
	if (info_bar != null) {
		INFO_BAR_OLD = $("div_info_bar").innerHTML;
		reset_info_bar();
	}
}

function toggle_all_chk(state) {
	
	var checkboxes = document.getElementsByTagName("input");
	var a;
	var list_id_part = "chk_listitem_";
	
	for (a = 0; a < checkboxes.length; a++) {
		if (checkboxes[a].id.substring(0, list_id_part.length) == list_id_part) {
			if (state != null) {
				set_chk(null, checkboxes[a].id, null, state);
			} else {
				set_chk(null, checkboxes[a].id, null, (!checkboxes[a].checked));
			}
		}
	}
	
	return false;
}

function set_chk(e, checkbox_id, toggle, state) {
	
	var checkbox = $(checkbox_id);
	
	try {
		e = e||window.event();
	} catch(e) { }
	
	if (checkbox == null || checkbox.type != "checkbox") {
		alert("set_chk(): Item with id '" + checkbox_id + "' must be a checkbox");
		return false;
	}
	
	if (toggle == true) {
		//toggle checkbox, instead of just looking at it's state
		if (checkbox.checked) {
			checkbox.checked = false;
			DATALIST_CHK_COUNT--;
		} else {
			checkbox.checked = true;
			DATALIST_CHK_COUNT++;
		}
	} else if (state != null) {
		//checkbox is being checked programatically
		if (state == true && !checkbox.checked) {
			DATALIST_CHK_COUNT++;
		} else if (state == false && checkbox.checked) {
			DATALIST_CHK_COUNT--;
		}
		
		checkbox.checked = state;
	} else {
		//checkbox is being checked manually
		if (checkbox.checked) {
			DATALIST_CHK_COUNT++;
		} else {
			DATALIST_CHK_COUNT--;
		}
	}
	
	//stop event bubbling
	if (window.event) {
		window.event.cancelBubble = true;
	} else if (e != null) {
		e.stopPropagation();
	}
	
	var infobar = $("div_info_bar");
	var row = $("listitem_" + checkbox.value);
	var btn = $("btn_multiact");
	var m_act = $("m_act");
	
	var select_multi_link = '|&nbsp;' +
		'<a href="#" onclick="return toggle_all_chk(true)">' + LCL_SELECT_ALL + '</a>' + '&nbsp;|&nbsp;' +
		'<a href="#" onclick="return toggle_all_chk()">' + LCL_INVERT + '</a>' + '&nbsp;|&nbsp;' +
		'<a href="#" onclick="return toggle_all_chk(false)">' +	LCL_UNSELECT_ALL + '</a>';
	
	if (checkbox.checked == true) {		
		//highlight item
		add_class(row, "selectHighlight");
	} else {
		//unhighlight item
		remove_class(row, "selectHighlight");
	}
	
	infobar.innerHTML = DATALIST_CHK_COUNT + " " + 
		ctx_pluralise(DATALIST_CHK_COUNT, LCL_ITEM, LCL_ITEMS) + 
		" " + LCL_SELECTED + " " + select_multi_link;
	
	if (DATALIST_CHK_COUNT <= 0) {
		//counter is 0 or lower (set to 0 anyway)
		reset_info_bar();
		DATALIST_CHK_COUNT == 0;
		
		//disable multi action controls
		if (btn != null) {
			btn.disabled = true;
			m_act.disabled = true;
		}
	} else {
		infobar.className = "highlight";
		
		//re-enable multi action controls
		if (btn != null) {
			btn.disabled = false;
			m_act.disabled = false;
		}
	}
}

function update_pager(pages_total, page, action, post_data, force_recreate, list_type) {
	
	var pagers = $a("div", "class", "pageSwitcher");
	var span_end;
	var classname, anchor, a, b, li;

	if (list_type == null)
		list_type = "list"; 
		
	page = parseInt(page);
	
	for (a = 0; a < pagers.length; a++) {	
		//insert/update paging buttons into each pager div detected
		if (pagers[a].childNodes.length == 0 || force_recreate == true) {
			var pager_container = document.createElement("ul");
			
			//add "first page" and "previous page" button, if required
			if (page > 1) {
				li = document.createElement("li");
				li.appendChild(new pager_button(a, "|&lt;", 1, false, action, post_data, list_type));
				pager_container.appendChild(li);
				
				if (page > 2) {
					li = document.createElement("li");
					li.appendChild(new pager_button(a, "&lt;", (page-1), false, action, post_data, list_type));
					pager_container.appendChild(li);
				}
			}
			
			if (pages_total > 1) {
				li = document.createElement("li");
				li.appendChild(new pager_select(a, page, pages_total, action, post_data));	
				pager_container.appendChild(li);
			}
			
			//add "last page" and "next page" button if required
			if (page < pages_total) {
				if (pages_total > 2) {
					//next page (if it doesn't just go to the last page)
					li = document.createElement("li");
					li.appendChild(new pager_button(a, "&gt;", (page+1), false, action, post_data, list_type));
					pager_container.appendChild(li);
				}
				
				li = document.createElement("li");
				li.appendChild(new pager_button(a, "&gt;|", pages_total, false, action, post_data, list_type));
				pager_container.appendChild(li);
			}
			
			DL_PAGE = page;
			
			
			pagers[a].innerHTML = "";
			pagers[a].appendChild(pager_container);
		} else {
			//simply select a new page number
			$("page_select_" + a).selectedIndex = (page - 1);
			
			DL_PAGE = page;
		}
	}
}

function pager_select(pager_num, page, pages_total, action, post_data) {
	var option
	
	this.select = document.createElement("select");
	this.select.id = "page_select_" + pager_num;
	
	//add pages, up to total
	for (a = 1; a <= pages_total; a++) {
		option = document.createElement("option");
		option.value = a;
		option.innerHTML = LANG['words']['page_singular'] + " " + a;
		
		if (page == a)
			option.selected = true;
		
		this.select.appendChild(option);
	}
		
	this.callback = function() { 
		get_data_list(
			null, 
			null, 
			action, 
			$("page_select_" + pager_num).options[$("page_select_" + pager_num).selectedIndex].value,
			post_data
		);
	};
		
	attach_event(this.select, "change", obj_event(this, "callback_exec", this.callback, true));
	
	return this.select;
}

pager_select.prototype.callback_exec = function(e, event_obj, click_function) {
	//switch a page
	click_function();
	source = get_source(e);
	source.blur();
}

function pager_button(pager_num, val, num, active, action, post_data, list_type) {
	//create a button
	this.page = num;
	
	if (num == null) {
		this.button = document.createElement("span");
		this.button.innerHTML = val;
	} else {	
		this.button = document.createElement("a");
		this.button.innerHTML = val;
		this.button.href = "#";
		this.button.id = "pagerButton_" + pager_num + "_" + this.page;
		
		if (active)
			this.button.className = "active";
		
		this.callback = function() { get_data_list(null, null, action, num, post_data) };
			
		attach_event(this.button, "click", obj_event(this, "callback_exec", this.callback, true));
	}
	
	return this.button;
}

pager_button.prototype.callback_exec = function(e, event_obj, click_function) {
	//switch a page
	click_function();
	source = get_source(e);
	source.blur();
}

function ctx_pluralise(number, str_singular, str_plural) {
	//sends back either str_singular or str_plural if number is plural
	if (!isNaN(number) && number == 1) {
		//use singular "items" word
		return str_singular;
	} else if (!isNaN(number)) {
		//use plural "items" word
		return str_plural;
	}
}



function update_search_field(search_val) {
	$("s_search").value = "0";
	$("s_search_button").value = LCL_SEARCH;
}

function search_records(xmldoc, response, action, post_data, list_node) {
	//send a search request (using the form "frm_search") to the specified action page
	if (xmldoc != null) {
		$("s_search_button").value = LCL_CANCEL;
	} else {
		var form = document.forms['frm_search'];
		var s_search = $("s_search");
					
		if (list_node != null) {
			DL_LIST_NODE = list_node;
		} else if (DL_LIST_NODE != null) {
			list_node = DL_LIST_NODE;
		}
		
		if (form == null) {
			alert("Search form 'frm_search' does not exist.");
			return false;
		}
		
		if (s_search == null) {
			alert("Search form does not contain an s_search field.");
			return false;
		}
		
		if (s_search.value == "1") {
			//cancel the search
			form.reset();
			s_search.value = "0";
			$("s_search_button").value = LCL_SEARCH;
			post_data += "&a=cancel_search&list_node=" + list_node
			
			ajax_send(null, arguments, PATH_ROOT + action, post_data);
		} else {
			//run a search
			form.elements['s_search'].value = "1";
			request_str = create_request_str("frm_search");
			post_data += "&" + request_str + "&a=search&list_node=" + list_node;
			
			ajax_send(search_records, arguments, PATH_ROOT + action, post_data);
				
			//store global list type
			DL_LIST_TYPE = "search";
		}
		return false;
	}
}



function autodate(element_id, e) {
	
	e = e || window.event();
	source = get_source(e);
	
	var dd = $(element_id + "_dd");
	var mm = $(element_id + "_mm");
	var yy = $(element_id + "_yy");
	var tt = $(element_id + "_tt");
	
	if (dd != null && mm != null && yy != null) {
		var dd_sel = (dd.selectedIndex != 0);
		var mm_sel = (mm.selectedIndex != 0);
		var yy_sel = (yy.selectedIndex != 0);
		
		var date_str = dd.options[dd.selectedIndex].value + "/" + 
			mm.options[mm.selectedIndex].value + "/" + 
			yy.options[yy.selectedIndex].value;
		
		//validate date
		if (!valid_date(date_str) && dd.selectedIndex > 1 && mm_sel && yy_sel) { 
			dd.selectedIndex --;
			autodate(element_id, e);
		}
	}
	
	//validate time
	if (tt != null) {
		var time = tt.value.split(":");
		
		if (time.length < 2)
			time = Array(tt.value, 00);
		
		if (time.length == 2) {
			time[0] = parseInt(time[0]);
			time[1] = parseInt(time[1]);
			
			if (isNaN(time[0])) {
				time[0] = 0;
			} else if (time[0] < 0) {
				time[0] = 0;
			} else if (time[0] > 23) {
				time[0] = 23;
			} else if (time[0] == 24) {
				time[0] = 23;
				time[1] = 59;
			}
		
			if (time[0].toString().length < 2) 
				time[0] = "0" + time[0];
			
			if (isNaN(time[1])) {
				time[1] = 0;
			} else if (time[1] < 0) {
				time[1] = 0;
			} else if (time[1] > 59) {
				time[1] = 59;
			}
		
			if (time[1].toString().length < 2)
				time[1] = "0" + time[1];
			
			tt.value = time[0] + ":" + time[1];
		}
	}
}

function valid_date(date_str) {
	
	var date_arr = date_str.split('/')
	
	var day = date_arr[0];
	var month = date_arr[1];
	var year = date_arr[2];
	
	var today = new Date();
	
	year = (!year) ? y2k(today.getYear()) : year;
	month = (!month) ? today.getMonth() : month - 1;
	
	if (!day) return false;
	
	var test = new Date(year, month, day);
	
	if ((y2k(test.getYear()) == year) && (month == test.getMonth()) && (day == test.getDate())) {
		return true;
	} else {
		return false;
	}
}

function y2k(number) { 
	
	return (number < 1000 ? number + 1900 : number);
}


function rowlist_hover(list_id) {
	
	var buttons_div = $("listbuttons_" + list_id);
	
	if (buttons_div == null)
		return false
	
	//turn off current buttons
	if (ROWLIST_CONTEXT_CONTAINER != null) {
		ROWLIST_CONTEXT_CONTAINER.style.display = "none";
	}
	
	buttons_div.style.display = "block";
	ROWLIST_CONTEXT_CONTAINER = buttons_div;

	ROWLIST_UNHOVER = false;

	TIMERS['rowlist_hover'] = window.setInterval(rowlist_unhover, "300");
}

function rowlist_unhover() {
	if (ROWLIST_UNHOVER != false) {
		var buttons = $("listbuttons_" + ROWLIST_UNHOVER);
		
		if (buttons != null)
			$("listbuttons_" + ROWLIST_UNHOVER).style.display = "none";
	}
		
	window.clearInterval(TIMERS['rowlist_hover']);
}

function rowlist_setunhover(list_id) {
	if (list_id != null) {
		ROWLIST_UNHOVER = list_id
	} else {
		ROWLIST_UNHOVER = false;
	}
}

function rowlist_reorder(xmldoc, response, record_id, action, direction) {
	
	if (xmldoc != null) {
		//let's move some rows! (not right now)
		
	} else {
		//send reorder request
		var post_data = "a=reorder&id=" + record_id + "&dir=" + direction;
		ajax_send(null, arguments, PATH_ROOT + action, post_data);
		return false;
	}
}

function pass_strength_score(password, username, forename, surname, email) {
	
	var score = 10, score_img, letter;
	password = password.trim();
	var password_lower = password.toLowerCase()
	
	var common_passwords = Array(
		"password",
		"god",
		"pass",
		"querty",
		"abc",
		"asdf",
		"123",
		"1234",
		"12345",
		"123456",
		"cloisters",
		"charlie",
		"thomas",
		"arsenal",
		"link182",
		"shiz",
		"test");
	
	//length - (length / 2) points for each character
	if (password.length > 0)
		score += password.length * (password.length / 2);
	
	//numbers
	num_count = password.match(/\d/g);

	if (password.match(/\d/) && password.match(/\D/)) {
		//7 points for each number if mixed with letters
		if (num_count != null)
			score += (num_count.length * 7);
	} else {
		//otherwise, 3 points per number
		if (num_count != null)
			score += (num_count.length * 3);
	}
	
	//10 points for mixed case
	if (password.match(/[a-z]/) && password.match(/[A-Z]/))
		score += 15;
		
	//10 points for each special character
	spec_char_count = password.match(/\W/g);
	
	if (spec_char_count != null)
		score += parseInt(spec_char_count.length) * 10;	
		
	//reset score to 10 for being the same as the username details
	if (password_lower == username.toLowerCase() 
		|| password_lower == forename.toLowerCase() 
		|| password_lower == surname.toLowerCase() 
		|| password_lower == email.toLowerCase()
		|| password_lower == email.toLowerCase().substr(0, password.length))
		score = 10;
	
	//reset score for common passwords
	if (in_array(password, common_passwords))
		score = 10;
	
	//reset score if all the characters are the same
	if (password.match(/^(\w)\1*$/))
		score = 10;
	
	if (score > 100)
		score = 100;
	
	return score;
}

function add_meta_element(xmldoc, response, tablename) {
	
	if (xmldoc == null) {
		if ($("metaPanel") == null) {
			alert("add_meta_element() - container with id 'metaPanel' does not exist!");
			return false;
		}
			
		var post_data = "a=load_meta_form&node=metaPanel&table=" + tablename.urlencode() +
			"&num=" + DYN_META_NUM;
		
		ajax_send(add_meta_element, arguments, PATH_ROOT + "/admin/actions.php", post_data);
		
		DYN_META_NUM++;
		return false;
	}
}

function load_meta_elements(xmldoc, response, tablename, record_id, show_internal) {
	
	panel = $("metaPanel");

	if (xmldoc != null) {
		//set rule count
		if (panel.childNodes.length > 0) {
			DYN_META_NUM = panel.childNodes.length + 1;
		}
	} else {
		if (panel == null) {
			alert("add_meta_element() - container with id 'metaPanel' does not exist!");
			return false;
		}
		
		var post_data = "a=load_meta_data&node=metaPanel&table=" + tablename.urlencode() +
			"&id=" + record_id;
		
		if (show_internal != null)
			post_data += "&show_internal=1";
			
		ajax_send(load_meta_elements, arguments, PATH_ROOT + "/admin/actions.php", post_data);
	}
}



function catfilter_show() {
	$("div_treeCats").className = "treeListExpanded";
	if (!SHOWN_CATS) catfilter_load();
	return false;
}

function catfilter_hide() {
	$("div_treeCats").className = "treeList";
	return false;
}



function pop_dialog(url, name, width, height) {
	
	if (name == null)
		name = "popdlg";
	
	if (width == null)
		width = "400";
	
	if (height == null)
		height = "380";
		
	var left = pop_get_left(width);
	var top = pop_get_top(height);
		
	var params = "width=" + width + ",height=" + height +
		",left=" + left + ",top=" + top + 
		",scrolling=auto,toolbars=no,menubars=no,resizable=no";
	
	if (!url.match(/https?:\/\//)) {
		var win = window.open(DOMAIN_FULL + url, name, params);
	} else {
		var win = window.open(url, name, params);
	}
}

function pop_get_left(width) {
	
	var win_width = new Number();
	var win_left = new Number();

	if (window.outerWidth) {
		win_width = window.outerWidth;
	} else {
		win_width = screen.width;
	}
	
	if (window.screenX) {
		win_left = window.screenX;
	} else {
		win_left = 0;
	}

	return Math.ceil(((win_width - width) / 2) + win_left);
}

function pop_get_top(height) {
	
	var win_height = new Number();
	var win_top = new Number();

	if (window.outerHeight) {
		win_height = window.outerHeight;
	} else {
		win_height = screen.height;
	}

	if (window.screenY) {
		win_top = window.screenY;
	} else {
		win_top = 0;
	}

	return Math.ceil(((win_height - height) / 2) + (win_top-30));
}



String.prototype.trim = function() {
	//removes whitespace from either side of a string
	return this.replace(/^\s+|\s+$/, "");
}

String.prototype.zerofill = function(length) {
	//fills zeros from the left in a string up to the required length specified
	var str = this;
	while (str.length < length) {
	        str = "0" + str;
	}
	return str;
}

String.prototype.ctx_tpl_name = function() {
	//alters string to be safe for use within the ctx templating system
	var str = this;
	str = str.replace(/\s/g, "_");
	str = str.replace(/\{\}\$:/, "");
	str = str.toLowerCase();
	return str;
}

String.prototype.ctx_path = function() {
	var str = this;
	str = str.replace(/[\/?#&]/g, "");
	str = str.replace(/\s/g, "_");
	str = str.toLowerCase();
	return str;
}

String.prototype.urlencode = function() {
	return encodeURIComponent(this);
}

function $(element_id) {
	
	if (document.getElementById(element_id)) {
		return document.getElementById(element_id);
	} else {
		return null;
	}
}

function $$(tag_name) {
	
	return document.getElementsByTagName(element_id);
}

function $a(tag_name, attribute, value, root_node) {
	
	root_node = root_node || document.body;
	var elements = root_node.getElementsByTagName(tag_name);
	var return_elements = new Array();
	
	for (var a = 0; a < elements.length; a++) {
		if (attribute == "class") {
			attr = elements[a].className;
		} else {
			attr = elements[a].getAttribute(attribute);
		}
		
		if (typeof(value) == "function" || typeof(value) == "object") {
			if (value.test(attr) == true)
				return_elements.push(elements[a]);
		} else {
			if ((attr != null && value == null) || attr == value)
				return_elements.push(elements[a]);
		}
	}
	
	return return_elements;
}

function tag_value(dom_root, tag_name) {
	
	var elements = dom_root.getElementsByTagName(tag_name)
	var val = null;
	
	if (elements.length > 1) {
		val = new Array();
		
		for (var a = 0; a < elements.length; a++) {
			if (elements[a].childNodes[0] != null)
				val.push(elements[a].childNodes[0].nodeValue);
		}
	} else if (elements.length == 1) {
		if (elements[0].childNodes[0] != null) {
			val = elements[0].childNodes[0].nodeValue;
		} else {
			val = null;
		}
	}
	
	return val;
}

function create_element(root_element_id, tagname, id, class_name) {
	//post_log("Creating " + tagname + " element with id: " + id + ", class: " + class_name
	//  + ", under element: " + root_element_id);
	//creates and returns an html DOM element
	var element;
	
	if (!$(id)) {
		if (root_element_id != null) {
			root_element = $(root_element_id)
		
			if (root_element == null) {
				alert("create_element() - Root element with ID '" 
					+ root_element_id + "' not found!");
				return false;
			}
		} else {
			var root_element = document.body;
		}
	
		var element = document.createElement(tagname);
	
		if (id != null) {
			element.setAttribute("id", id);
		}
	
		root_element.appendChild(element);
	} else {
		//if it already exists, return it
		element = $(id);
	}

	if (class_name != null) {
		element.className = class_name;
		element.setAttribute("class", class_name);
	}
	
	return element;
}

function destroy_element(element, quiet, reinstate_selects) {
	//removes an element by ID
	if (typeof(element) == "string")
		element = $(element);
	
	if (quiet == null)
		quiet = true;
		
	if (element == null && quiet) {
		return false;
	} else if (element == null && !quiet) {
		alert("destroy_element() - Element not found!");
	}
		
	var element_arr = Array();
	//element_arr['next_sibling'] = element.nextSibling;			
	element_arr['parent_node'] = element.parentNode;
	
	//remove parent if it's a dynamic DOM container (and now empty), or just remove element
	if (element.parentNode.className == "dynDomContainer" 
		&& element.parentNode.childNodes.length == 1) {
		element_arr['node'] = element.parentNode.parentNode.removeChild(element.parentNode);
	} else {
		element_arr['node'] = element.parentNode.removeChild(element);
	}
	
	//store element in deleted nodes array
	DOM_CACHE[element.id] = element_arr;
	
	if (reinstate_selects) ie6_top_objects(false);
	
	return false;
}

function restore_element(element_id, highlight) {
	
	if (DOM_CACHE[element_id] != null) {
		var element = DOM_CACHE[element_id]['parent_node'].appendChild(DOM_CACHE[element_id]['node']);
		
		if (highlight) {
			//highlight the element with a fun colour
			highlight_element(element);
		}
	}
}

function destroy_element_children(element) {
	
	if (typeof(element) == "string")
		element = $(element);
		
	if (element != null) {
		while (element.firstChild) {
			element.removeChild(element.firstChild);
		}
	} else {
		alert("destroy_element_children() - Element not found!");
	}
}

function switch_element_parent(element_id, new_parent_id, new_id) {
	//moves an element from one parent to another, with the option
	//to give it a new id specieid by new_id
	var element, new_parent;

	if (element = $(element_id)) {
		element = element.parentNode.removeChild(element);

		if (new_parent = $(new_parent_id)) {
			new_parent.appendChild(element);

			if (new_id != "") {
				element.setAttribute("id", new_id);
			}

			return element;
		} else {
			alert("switch_element_parent() - New parent element with ID '" +
			 	new_parent_id + "' not found!");	
		}
	} else {
		alert("switch_element_parent() - Element with ID '" + element_id + "' not found!");
	}
}

function copy_element(element_id, new_id) {
	//copies an element, gives it a new ID and returns it
	var element, new_parent;

	if (element = $(element_id)) {
		
		new_element = element.cloneNode(true);
		
		if (!$(new_id) && new_id != "") {
			new_element.setAttribute("id", new_id);
		} else {	
			alert("copy_element() - New ID '" + new_id + "' alredy exists or is blank!");
			return false;
		}
		
		return new_element;
	} else {
		alert("copy_element() - Element with ID '" + element_id + "' not found!");
	}
}

function element_left_pos(element) {
	var left = 0;
	
	while (element.offsetParent) {
		left += element.offsetLeft;
		element = element.offsetParent;
	}

	return left;
}

function get_source(e) {
	if (e.srcElement) {
		return e.srcElement;
	} else if (e.target) {
		return e.target;
	} else {
		return false;
	}
}

function element_top_pos(element) {
	var	top = 0;
	
	while (element.offsetParent) {
		top += element.offsetTop;
		element = element.offsetParent;
	}

	return top;
}

function scroll_left() {
	if (document.documentElement) {
		if (document.body.scrollLeft != 0) {
			return document.body.scrollLeft;
		} else if (document.documentElement.scrollLeft != 0) {
			return document.documentElement.scrollLeft;
		} else {
			return 0;
		}
	} else {
		return window.pageXOffset;
	}
}

function scroll_top() {
	if (document.documentElement) {
		if (document.body.scrollTop != 0) {
			return document.body.scrollTop;
		} else if (document.documentElement.scrollTop != 0) {
			return document.documentElement.scrollTop;
		} else {
			//uff :(
			return 0;
		}
	} else {
		return window.pageYOffset;
	}
}

function get_cursor_pos(e) {
	var posx = 0;
	var posy = 0;
	
	e = e||window.event;
	
	if (e.pageX || e.pageY) {
		posx = e.pageX;
		posy = e.pageY;
	} else if (e.clientX || e.clientY) 	{
		posx = e.clientX + document.body.scrollLeft	+ document.documentElement.scrollLeft;
		posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	}
	
	return Array(posx, posy);	
}

function attach_event(element, event_name, action) {
	
	if (element.addEventListener) {
		element.addEventListener(event_name, action, false);
	} else {
		element.attachEvent("on" + event_name, action);
	}
	
	
	if (element.attachEvent) {
		window.attachEvent("onunload", function() {
				try {
					//heh, IE 6 eh?
					detach_event(element, event_name, action);
				} catch(e) {}
			});
	}
}

function detach_event(element, event_name, action) {
	
	if (typeof(element) == "string") {
		element = $(element);
	}
	
	if (element != null) {

		if (window.detachEvent) {
			element.detachEvent("on" + event_name, action);
		} else {
			element.removeEventListener(event_name, action, true);
		}
	} else {
		alert("remove_event() - Element with ID '" + element + "' not found!");
	}

}

function obj_event(obj, method_name, args, prevent_default) {
	
	return (function(e){
		e = e||window.event;

		if (prevent_default) {
			try {
				e.preventDefault();
			} catch(err) {
				e.returnValue = false;
			}
		}
		
		if (obj[method_name] == undefined) {
			alert("obj_event(): method " + method_name + " doesn't exist");
			return false;
		} else {
			return obj[method_name](e, this, args);
		}
	});
}

function func_event(func, args, prevent_default) {
	
	return (function(e){
		e = e||window.event;

		if (prevent_default) {
			try {
				e.preventDefault();
			} catch(err) {
				e.returnValue = false;
			}
		}
		
		return func(e, this, args);
	});
}

function add_class(element, class_name) {
	//adds a classname element to an html tag <tag class="[foo bar whatsit]">

	//make sure the class doesn't already exist
	remove_class(element, class_name);

	if (element.className != "") {
		element.className = element.className + " " + class_name;
	} else {
		element.className = class_name;			
	}
}

function switch_class(element, class_name, new_class_name) {
	
	re = new RegExp(class_name, "i")
	element.className = element.className.replace(re, new_class_name);
}

function remove_class(element, class_name) {
	//removes class_name class from an element with element_id ID
	var new_class_def = "", element;
	
	classArray = element.className.split(" ");

	for (var a = 0; a < classArray.length; a++) {
		if (classArray[a] != class_name)
			new_class_def += classArray[a] + " ";
	}

	new_class_def = new_class_def.trim();
	element.className = new_class_def;
}

function remove_all_classes(element, exception) {
	//removes all classes from an element with element_id ID, except for that matching exception
	var new_class_name = "", element;
	
	classArray = element.className.split(" ");

	for (var a = 0; a < classArray.length; a++) {
		if (classArray[a] == exception) {
			new_class_name += classArray[a] + " ";
		}
	}

	new_class_name = new_class_name.trim();
	element.className = new_class_name;
}

function class_exists(element, class_name) {
	//checks if an html tag has a specific classname applied to it.
	if (element.className.indexOf(" ") != -1) {
		//multiple classnames within one element
		classes = element.className.split(" ");

		for (var a = 0;  a < classes.length; a++) {
			if (classes[a] == class_name)
				return true;
		}
	} else {
		//single classname
		if (element.className == class_name)
			return true;
	}
	
	return false;
}

function element_display(element, display_type) {
	
	if (typeof(element) == "string")
		element = $(element);
	
	//convert true/false to block/none.
	if (display_type === true) {
		display_type = "block";
	} else if (display_type === false) {
		display_type = "none";
	}
	
	if (IS_IE6) {
		//ie6 specific code
		if (typeof(element.style.styleFloat) != "undefined" && display_type == "block") {
			//force floated elements display type to inline when using IE6 (heh)
			element.style.display = "inline";
		} else {
			//use original display type
			element.style.display = display_type;
		}
	} else {
		element.style.display = display_type;
	}
}

function enable_form_elements(container, enabled) {
	//enables or disables form elements within the specified container
	elements = container.getElementsByTagName("INPUT");
	
	for (a = 0; a < elements.length; a++) {
		elements[a].disabled = (!enabled);
	}
	
	elements = container.getElementsByTagName("TEXTAREA");
	
	for (a = 0; a < elements.length; a++) {
		elements[a].disabled = (!enabled);
	}
	
	elements = container.getElementsByTagName("SELECT");
	
	for (a = 0; a < elements.length; a++) {
		elements[a].disabled = (!enabled);
	}
}

function ie6_top_objects(remove) {
	//remove all SELECT elements, because IE 6 is a big fat pile of [deleted]
	if (!IS_IE6) return;	
	
	var elements = document.getElementsByTagName("SELECT");
	var a;
	
	for (a = 0; a < elements.length; a++) {
		if (remove) {
			//remove all objects
			elements[a].style.visibility = "hidden";
		} else {
			//show all objects
			elements[a].style.visibility = "visible";
		
		}
	}
}

function max_size_obj(element) {
	
	var x, y, left, top;
	
	if (self.innerHeight != null) {
		//fx, safari
		if (document.body.offsetHeight > self.innerHeight) {
			y = self.innerHeight + (document.body.offsetHeight - self.innerHeight);
		} else {
			y = self.innerHeight;
		}
	} else if (document.body.clientHeight != null) {
		//ie
		x = document.body.clientWidth;
		
		if (document.body.offsetHeight > document.documentElement.clientHeight) {
			y = document.documentElement.clientHeight + (document.body.offsetHeight - document.documentElement.clientHeight);
		} else {
			y = document.documentElement.clientHeight;
		}
		element.style.width = x + "px";
	}
		
	element.style.top = "0";
	element.style.left = "0";
	element.style.height = y + "px";
}

function switch_inset(inset_id) {
	
	var inset = $(inset_id);
	var switch_button;
	
	//find inner div and switch button
	for (a = 0; a < inset.childNodes.length; a++) {
		switch (inset.childNodes[a].tagName) {
			case "DIV":
				inner_div = inset.childNodes[a];
				break;
			case "A":
				if (switch_button == null)
					switch_button = inset.childNodes[a];
				break;
		}
	}
	
	if (switch_button == null) {
		alert("Inset div switch button not found. An anchor element must exist as a child node of the outer inset div");
		return false
	}
	
	//lose focus from the switch button because the selection highlight is ugly at this size
	switch_button.blur();
	
	if (inner_div != null) {
		if (inner_div.className == "hidden") {
			inner_div.className = "innerInsetContent";
			switch_class(switch_button, "innerInsetSwitch", "innerInsetSwitchActive");
		} else {
			inner_div.className = "hidden";
			switch_class(switch_button, "innerInsetSwitchActive", "innerInsetSwitch");
		}
	} else {
		alert("switch_inset(): element with id '" + inset_id + "' does not exist!");
	}
	
	return false;
}

function order_row(row_id, x, allow_loop) {
		
	var element = document.getElementById(row_id);
	var table = element.parentNode;
	var new_index = parseInt(element.rowIndex + x);
	
	if ((new_index < 0 || new_index == table.rows.length) && allow_loop == null)
		//stop looping, if requested
		return false;
	
	//allow for looping of the rows
	if (new_index < 0 ) new_index += table.rows.length;
	if (new_index == table.rows.length ) new_index = 0;
	
	//remove and replace at new index
	table.removeChild(element);
	
	var new_row = table.insertRow(new_index);
	table.replaceChild(element, new_row);
}

function order_element(element, x) {
	
	//get element parent
	var parent = element.parentNode;
	var current_index = 0;
	
	if (class_exists(parent, "dynDomContainer")) {
		//element is a process_xml_standard inserted node and its auto-container 
		//should be referenced instead
		element = element.parentNode;
		parent = parent.parentNode;
	}
	
	if (parent == null)
		//cannot reorder the top level element (what on earth for anyway?)
		return false;
		
	if (parent.childNodes.length == 1)
		//no point in ordering one element!
		return false;
		
	//number of child nodes
	var parent_num = parent.childNodes.length;
		
	//get current index
	for (a = 0; a < parent_num; a++) {
		if (parent.childNodes[a] == element) {
			current_index = a;
			break;
		}
	}
	
	new_index = current_index + parseInt(x);
	
	if (new_index < 0) new_index += parent.childNodes.length;
	if (new_index >= parent.childNodes.length) new_index = 0;
	
	//remove element for replacement
	element = parent.removeChild(element);
	
	//place back at correct index
	if (new_index == (parent_num - 1)) {
		parent.appendChild(element);
	} else {
		for (a = 0; a < parent_num; a++) {
			if (a == new_index) {
				parent.insertBefore(element, parent.childNodes[a]);
				break;
			}
		}
	}
	
	return false;
}

function enter_element_orders(input_collection) {
	
	if (input_collection != null) {
		for (a = 0; a < input_collection.length; a++)
			input_collection[a].value = (a + 1);
	}
}

function breadcrumb(parent_node, breadcrumb) {
	
	while (parent_node.firstChild)
		parent_node.removeChild(parent_node.firstChild);
		
	for (a = breadcrumb.length - 1; a >= 0; a--)
		parent_node.appendChild(new this.node(this, breadcrumb[a], a, breadcrumb.length));
}

breadcrumb.prototype.node = function(parent_obj, link, num, total) {
	//create a node link	
	this.node = document.createElement("a");
	this.node.className = "crumb";
	this.node.id = "nodeLink" + num;
	this.node.href = link['href'];
	
	if (num < (total - 1)) {
		//all but the first node
		img_path = PATH_ROOT + "/templates/admin/images/rte_tools/node_chevron_int.gif";
	} else {
		//the first node looks different
		img_path = PATH_ROOT + "/templates/admin/images/rte_tools/node_chevron.gif";
	}
	
	if (num == 0) {
		//last node gets a right border
		this.node.style.borderRight = "1px solid #999";
	}
	
	this.node.style.background = "url('" + img_path + "') no-repeat left top";
	
	//create internal span for text and background image
	this.node_label = document.createElement("span");
	
	var label_text = link['label'].replace(/\s/g, "&nbsp;");
	this.node_label.innerHTML += label_text;
	
	if (link['label'].length < 2) {
		this.node_label.style.paddingRight = "6px";
	}
	
	this.node.appendChild(this.node_label);
		
	if (IS_IE6) {
		//ahh, the ever wonderous ie6 decides that float = 100% wide!
		this.node.style.width = this.node_label.offsetWidth;
	}
	
	if (link['onclick'] != null)
		//attach_event(this.node, "click", link['onclick']);
		attach_event(this.node_label, "click", obj_event(parent_obj, "nodeclick", link['onclick'], true));
	
	attach_event(this.node_label, "mouseover", obj_event(parent_obj, "nodeover", null, true));
	attach_event(this.node_label, "mouseout", obj_event(parent_obj, "nodeout", null, true));
	
	return this.node;
}

breadcrumb.prototype.nodeover = function(e, event_obj, args) {
	source = get_source(e);
	source.style.backgroundPosition = "12px bottom";
	source.parentNode.style.backgroundPosition = "left bottom";
	
	//hover next node
	if (source.parentNode.nextSibling) {
		source.parentNode.nextSibling.style.backgroundPosition = "left 50%";
	}
}


breadcrumb.prototype.nodeout = function(e, event_obj, args) {
	source = get_source(e);
	source.style.backgroundPosition = "12px top";
	source.parentNode.style.backgroundPosition = "left top";
	
	//unhover next node
	if (source.parentNode.nextSibling) {
		source.parentNode.nextSibling.style.backgroundPosition = "left top";
	}
}

breadcrumb.prototype.nodeclick = function(e, event_obj, callback) {
	callback();
}



function textarea_tab(e, element) {
	var tab = 9;
	var tab_str = String.fromCharCode(tab);
	
	e = e || window.event();
	
	if (e.ctrlKey || e.shiftKey)
		return false;
		
	scroll_pos = element.scrollTop;

	if (e.keyCode == tab && get_source(e) == element) {
		
		
		textarea_insert_at_selection(element, tab_str)
		
		element.scrollTop = scroll_pos;
	
		try {
			e.preventDefault();
		} catch(err) {
			e.returnValue = false;
		}
	
		return false;
	}
}

function textarea_insert_at_selection(element, text, cursor_after) {
	
	if (document.selection) {
		//ie
		element.focus();
		
		if (document.selection.type != "Control") {
			element.selection = document.selection.createRange();
			element.selection.text = text;
		}
	} else if (window.getSelection) {
		//firefox, safari
		var start = element.selectionStart;
		var end = element.selectionEnd;
		
		element.value = element.value.substring(0, start) +  
			text +
			element.value.substring(end, element.value.length);
		
		element.focus();
		
		if (cursor_after == true) {
			element.selectionStart = start + text.length;
			element.selectionEnd = start + text.length;
		} else {
			element.selectionStart = start+1;
			element.selectionEnd = start+1;
		}
	}
}



function add_select_value(select, text, value, collision_action, select_after) {
	
    var option = new Option(text, value, true, true);
    var length = select.length;
	var a;
	
	//if the element is only 1 high, unselect everything else
	if (!select.height) {
		for (var i = 0; i < select.options.length; i++) {
			select.options[i].selected = false;
		}
	}
	
	if (collision_action !== false && collision_action !== null) {
		//detect multiples
		collision_num = parseInt(collision_action);
		found_existing = false;
		
		for (a = 0; a < select.length; a++) {
			if (collision_num == 1 || collision_action === true) {
				//enforce unique values
				if (select.options[a].value == value) {
					found_existing = true;
					break;
				}
			} else if (collision_num == 2) {
				//enforce unique labels
				if (select.options[a].text == text) {
					found_existing = true;
					break;
				}			
			} else if (collision_num == 3) {
				//enforce unique combined values & labels
				if (select.options[a].value == value && select.options[a].text == text) {
					found_existing = true;
					break;
				}
			}
		}
		
		if (found_existing) {
			if (select_after == true)
				select.options[a].selected = true;
			return false;
		}
	}
	
   	select.options[length] = option;
	
	if (select_after == true) {
		select.options[length].selected = true;
	} else {
		select.options[length].selected = false;
	}
	
	//delete the pre-set options
	for (var i = 0; i < select.options.length; i++) {
		if (select.options[i].value == "REMOVE_ON_UPDATE") {
			select.options[i] = null;
		}
	}
}

function remove_select_value(select, value) {
	
	var sel = select.selectedIndex;

	for (var a = (select.options.length - 1); a >= 0; a--) {
		if (select.options[a].value == value) {
			select.options[a] = null;
		}
	}
	
	//select element after it (if something was selected)
	if (sel != null && sel != -1) {
		if (sel < select.options.length) {
			select.selectedIndex = sel;
		} else {
			select.selectedIndex = (select.options.length - 1);
		}
	}
}

function remove_select_index(select, index) {
	
	var sel = select.selectedIndex;

	select.options[index] = null;	
	
	//select element after it (if something was selected)
	if (sel != null && sel != -1) {
		if (sel < select.options.length) {
			select.selectedIndex = sel;
		} else {
			select.selectedIndex = (select.options.length - 1);
		}
	}
}

function wipe_select(select_element) {
	//removes all the <option>s from a <select> element
	select_element.length = 0;
}

function select_all_select(select_element) {
	//selects every element within a multiple choice selector
	for (var a = 0; a < select_element.options.length; a++)
		select_element.options[a].selected = true;
}

function select_val(select, value, add_text) {
	
	for (var a = 0; a < select.length; a++) {
		if (select.options[a].value == value) {
			select.selectedIndex = a;
			return true;
		}
	}
	
	//if the function gets here the item doesn't exist - so add it!
	if (add_text != null) {
		add_select_value(select, add_text, value);
		select_val(select, value);
	}
}

function get_select_val(select) {
	if (typeof(select) == "string")
		select = $(select);
	
	return select.options[select.selectedIndex].value;
}



function radio_val(radio, value) {
	for (a = 0; a < radio.length; a++) {
		if (radio[a].value == value) {
			radio[a].checked = true;
		} else {
			radio[a].checked = false;
		}
	}
}



function checkbox_val(checkbox, value) {
	if (checkbox.value == value) {
		checkbox.checked = true;
	} else {
		checkbox.checked = false;
	}
}



function xml(callback, callback_args) {
	
	this.callback = callback;

	if (callback_args != null) {
		//callback arguments have been set
	    if (callback_args.length > 0) {	
			this.callback_args = callback_args;
		} else {
			//no arguments set
			this.callback_args = new Array();
		}
	} else {
		//no arguments defined
		this.callback_args = new Array();
	}
	
	this.notify = null;
	this.request_data = null;
	this.request_url = null;
	this.request_type = null;
	
	if (window.XMLHttpRequest) {
		//w3 compliant browsers...
		this.xml_obj = new XMLHttpRequest();
		
		if (this.xml_obj.overrideMimeType)
			this.xml_obj.overrideMimeType("text/xml; charset=UTF-8");	
	} else if (window.ActiveXObject) {
		//... and IE
		try {
			//new XMLhttp object
			this.xml_obj = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (error) {
			//old XMLhttp object
			try {
				this.xml_obj = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (error) {
				alert("create_request_obj() - Could not create an XMLHTTP instance");
				return false;
			}
		}
	}
	
	//set callback	
	this.xml_obj.onreadystatechange = obj_event(this, "exec_callback");
	
	attach_event(window, "unload", obj_event(this, "cancel", null));

	return this;
}

xml.prototype.open = function(request_type, request_url, async) {
	this.request_url = request_url;
	this.request_type = request_type;
	this.xml_obj.open(request_type, request_url, async);
}

xml.prototype.header = function(name, value) {
	//create a header
	this.xml_obj.setRequestHeader(name, value);
}

xml.prototype.post = function(request_data) {
	//post the request data
	this.request_data = request_data;
	
	//add required post token
	request_data += "&ctx_token=" + POST_TOKEN;
	
	//add referer
	request_data += "&ctx_ref=" + location.pathname;
	
	this.xml_obj.send(request_data);
	this.pre_send(this);
}

xml.prototype.cancel = function() {
	//cancel this object's http request - not currently working in IE 6 - needs debugging
	//alert(this.xml_obj.onreadystatechange);
	//this.xml_obj.onreadystatechange = null;
	//this.xml_obj.abort();
}

xml.prototype.exec_callback = function() {		
	if (this.xml_obj.readyState == 4) {
		//request object has loaded
		
		//hide loading div
		this.post_send();
		
		if (this.xml_obj.status == 200) {
			//document loaded correctly return everything under the root node
			//as an XML DOM object
			this.xml = this.xml_obj.responseXML;
			
			if (this.xml != null) {
				this.root = this.xml.getElementsByTagName("root");

				if (this.root[0] != null) {
					result = process_xml_standard(this.root[0]);
					
					if (result != false) {
						//only execute callback if the "last" element wasn't specified as a pXML node
						this.callback_args[0] = this.root[0];
						
						if (this.callback != null)
							this.callback.apply(this, this.callback_args);
					}
				} else {
					ajax_error(this.xml_obj, this.xml_obj.readyState);
					return false;
				}
			} else {
				ajax_error(this.xml_obj, this.xml_obj.readyState);	
			}
		} else {
			//something unexpected happened
			ajax_error(this.xml_obj, this.xml_obj.readyState);
			return false;
		}
	}
}

xml.prototype.pre_send = function(parent, display) {
	//notifies the user of an xmlhttp object request if it's taking too long
	if (display) {
		this.notify = create_element(null, "DIV", null, "ajaxNotify");
		
		this.label = document.createElement("DIV");
		this.label.className = "ajaxNotifyLabel";
		this.label.innerHTML = LCL_LOADING + "...<br/>";
		
		if (DEBUG) {
			this.label.innerHTML += "Bytes Sent: " + this.request_data.length + "<br/>";
			this.label.innerHTML += "URI: " + this.request_url + " (" + this.request_type + ")<br/>";
		}
		
		this.notify.style.top = (scroll_top() + 5) + "px";
		
		this.notify.appendChild(this.label);
		
		this.anim_container = document.createElement("DIV");
		this.anim_container.className = "ajaxNotifyProgress";
		
		this.notify.appendChild(this.anim_container);
		
		//show waiting cursor
		document.body.style.cursor = "wait";
		
		attach_event(document, "scroll", obj_event(this, "notify_position"));
	} else {
		this.timer = window.setTimeout(function() {
				parent.pre_send(parent, true);
			}, 1000);
		this.timer2 = window.setTimeout(function() {
				parent.send_timeout(parent);
			}, 30000);
			
		//show progress icon, if exists
		var progress_icon = $("xml_progressicon");
		
		if (progress_icon != null)
			element_display(progress_icon, "block");
	}
}

xml.prototype.post_send = function() {
	//run this function after completing an xmlhttp request
	if (this.notify != null)
		this.notify.parentNode.removeChild(this.notify);
	
	window.clearTimeout(this.timer);
	window.clearTimeout(this.timer2);
	
	//reset cursor
	document.body.style.cursor = "default";
	
	detach_event(document, "scroll", obj_event(this, "notify_position"));
	this.notify = null;
	
	//hide progress icon, if exists
	var progress_icon = $("xml_progressicon");
			
	if (progress_icon != null)
		element_display(progress_icon, "none");
}

xml.prototype.notify_position = function() {
	if (this.notify != null)
		this.notify.style.top = (scroll_top() + 5) + "px";
}

xml.prototype.send_timeout = function(parent) {
	//xml is taking too long - time for a time out	
	//clear timer and notify panel
	parent.post_send();
	
	//(try to) clear the xml object
	parent.xml_obj.onreadystatechange = null;
	parent.xml_obj = null;
	
	//reset cursor
	document.body.style.cursor = "default";
	
	alert("The server has not responded to your request, and Cortex has stopped waiting." + 
		"This may be due to heavy network traffic. Please wait and try again.\n\n");
}

function ajax_send(callback, callback_args, request_url, request_data, request_type) {
	
	var xml_obj = new xml(callback, callback_args);
	var a;
	
	if (request_url == null || request_url == "undefined") {
		alert("ajax_send() - AJAX request called without a request URI");
		return false;
	}
	
	if (request_type == null) {
		request_type = "POST";
	} else if (request_type != "POST" && request_type != "GET") {
		alert("Invalid request type '" + request_type + "'. Use GET or POST");
		return false;
	}
	
	if (request_data == null)
		request_data = new String();
	
	//if (callback != null) {
		//callback_function = callback.toString();
		//callback_function = callback_function.replace(/function ([^\(]*)(.|\n)*/i, "$1");
	//}
	
	//open the connection, either GET or POST
	xml_obj.open(request_type, request_url, true);
	
	//send the headers
	xml_obj.header("Accept", "text/xml");
	xml_obj.header("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");	
	xml_obj.header("Content-length", request_data.length);
	xml_obj.header("Connection", "close");

	//send the GET or POST data
	xml_obj.post(request_data);
}

function ajax_send_delayed(callback, callback_args, request_url, request_data, timeout, request_type) {
	
	if (AJAX_QUEUE[request_url] != null)
		window.clearTimeout(AJAX_QUEUE[request_url]);
	
	AJAX_QUEUE[request_url] = window.setTimeout(function() {
		ajax_send(callback, callback_args, request_url, request_data, request_type);
	}, timeout);
}

function ajax_error(ajax_obj) {
	//display an ajax related error
	if (ajax_obj.status == "404") {
		alert(ajax_obj.responseText);
	} else {
		alert_msg = '<h3>An XML root node was not returned.</h3>' +
			'Below is the response recieved:</p>' + 
			'<p>Content-Type: ' + ajax_obj.getResponseHeader("Content-Type") + '<br/>' + 
			'Date: ' + ajax_obj.getResponseHeader("Date") + '<br/>' + 
			'Server: ' + ajax_obj.getResponseHeader("Server") + '<br/>' + 
			'Connection: ' + ajax_obj.getResponseHeader("Connection") + '<br/>' + 
			'Object: ' + ajax_obj + ' (' + typeof(ajax_obj) + ')<br/>' +
			'Status: ' + ajax_obj.status + '<br/>' + 
			'ReadyState:' + ajax_obj.readyState + '</p>' +
			'<p>Response Text:</p>' + 
			'<pre>' + ajax_obj.responseText + '</pre>';
			
		root_node = create_element("abs_container", "DIV", "ajaxErrorContainer", "absObjectInner");
		root_node.innerHTML = alert_msg;
			
		var button = document.createElement("INPUT");
		button.type = "button";
		button.value = "Close";
		
		root_node.appendChild(button);
		
		attach_event(button, "click", function() { destroy_element('ajaxErrorContainer', true, true); });
		
		ie6_top_objects(true);
		anim_scroll(0, parseInt(root_node.offsetTop) - 20);
	}
	//alert(alert_msg);
}

function process_xml_standard(xmldoc) {
	
	var a, b, node, uri, wait_time
	var type, form_name, form_id, form_element, focus;
	
	if (xmldoc == null) return false;
	
	if (xmldoc.getElementsByTagName("noparse").length > 0)
		//no parsing required by this function
		return true;

	for (a = 0; a < xmldoc.childNodes.length; a++) {
		//loop through all sub-nodes of <root>
		node = xmldoc.childNodes[a];
		
		//alert("node " + a + " of " + xmldoc.childNodes.length + ": " + node.tagName);
		
		switch (node.tagName) {
			case "js_run":
				//execute custom javasript
				if (node.childNodes[0] != null) {
					var function_str = node.childNodes[0].nodeValue;
					var js_run = new Function(function_str);
					js_run();
				}
				
				break;
				
			case "token_update":
				if (node.childNodes[0].nodeValue != null)
					POST_TOKEN = node.childNodes[0].nodeValue;
				break;
				
			case "js_alert":
				//standard alert() message
				alert(node.childNodes[0].nodeValue);
				break;
				
			case "alert":
				//ctx_alert() message
				//get buttons
				var buttons = node.getElementsByTagName("button");
					
				if (buttons.length > 0) {
					//buttons exist
					var button_arr = new Array();
					var button;
					
					for (var b = 0; b < buttons.length; b++) {
						button = new Array();
						button['onclick'] = buttons[a].getAttribute("onclick");
						button['value'] = buttons[a].getAttribute("value");
						button['focus'] = buttons[a].getAttribute("focus");
						
						button_arr.push(button);
					}
				} else {
					var button_arr = null;
				}
				
				//send alert
				new ctx_alert(node.childNodes[0].nodeValue, button_arr);
				break;
			
			case "redirect":
				//redirect to another page after the set time
				wait_time = node.getAttribute("wait");
				uri = node.getAttribute("href");

				if (wait_time != null) {
					page_redirect(uri, wait_time);
				} else {					
					page_redirect(uri, 0);
				}
				break;
			
			case "post":
				//post a form
				form_id = node.getAttribute("form_id");
				uri = node.getAttribute("action");
				strip_js = node.getAttribute("strip_js");
					
				if ($(form_id)) {
					var form =$(form_id);
					
					if (strip_js != null)
						form.onsubmit = null;
					
					if (uri != null) {
						form.action = uri;
						form.submit();
					} else {
						form.submit();						
					}
				}
				break;
			
			case "destroy":
				//destroy the specified element
				var element_id = node.getAttribute("id");
				var reinstate_selects = node.getAttribute("reinstate_selects");
				var unshade = node.getAttribute("unshade");
				
				if (reinstate_selects == "true") {
					reinstate_selects = true
				} else {
					reinstate_selects = false;
				}
				
				destroy_element(element_id, true, reinstate_selects);
				
				if (unshade != null)
					ctx_unshade();
					
				break;
				
			case "focus":
				//focus() the specified element
				var element_id = node.getAttribute("id");
				if (element = $(element_id)) {
					element.focus();
				} else {
					alert("process_xml_standard() Focus element with id '" + element_id +
						"' does not exist");
				}
				break;
			
			case "template":
				//load a template file from the given uri
				if (node.childNodes.length == 0) //empty template
					break;
					
				var html = node.childNodes[0].childNodes[0].nodeValue;
				var root_node = node.getAttribute("root");
				var id = node.getAttribute("id");
				var replace_id = node.getAttribute("replace_id");
				var valign = node.getAttribute("valign");
				var align = node.getAttribute("align");
				var append = node.getAttribute("append");
				var root_top = node.getAttribute("root_top");
				var anim = node.getAttribute("anim");
				
				var tpl_type = null;
				
				if (root_node == null || root_node == undefined) {
					if (id == null)
						id = "int_dialog";
					
					tpl_type = "popup";
					destroy_element(id, true);
					var abs_container = $("abs_container");
					root_node = create_element("abs_container", "DIV", id, "absObjectInner");
					
					if (root_top != null) {
						root_node.style.top = (parseInt(root_top) + (scroll_top() + 10)) + "px";
					} else {
						root_node.style.top = (scroll_top() + 10) + "px";
					}
					
					root_node.style.visibility = "hidden";
					
					//anim_scroll(0, parseInt(root_node.offsetTop) - 20);
					ie6_top_objects(true);
					ctx_shade();
				} else {
					tpl_type = "static";
					
					if (!$(root_node)) {
						alert("process_xml_standard():template: Root element '" + root_node + "' not found.");
						return false;
					} else {
						root_node = $(root_node);
					}
				}
				
				outer = document.createElement("DIV", "", "");
				outer.innerHTML = html;
				outer.className = "dynDomContainer";
				
				if (replace_id != null)
					var replace_node = $(replace_id);
				
				if (replace_node != null) {
					//check for a dynDomContainer element above the replacenode
					if (replace_node.parentNode.className == "dynDomContainer")
						replace_node = replace_node.parentNode;
						
					//replace a specific element
					root_node.replaceChild(outer, replace_node);
				} else {
					//place inside the parent as normal
					if (append == "append") {			
						//append content to selected container
						root_node.appendChild(outer);
					} else {					
						//replace contents of container
						while (root_node.firstChild)
							root_node.removeChild(root_node.firstChild);
						
						root_node.appendChild(outer);
					}
				}
				
				if (tpl_type == "popup") {
					//position popup template items
					root_node.style.left = 
						Math.round(((abs_container.offsetWidth / 2) - (root_node.offsetWidth / 2))) + "px";
					root_node.style.visibility = "visible";
				}
				
				if (DRAG_OBJ !== null)
					DRAG_OBJ.detect_divs(outer);
				
				if (anim == "slide") {
					slidein_element(root_node, "block");
				} else if (anim == "fade") {
					reveal_element(root_node, "block");
				}
				
				break;
				
			case "edit_dom":
				//edit nodes within the DOM
				var element, dom_element, element_val;
				
				for (b = 0; b < node.childNodes.length; b++) {
					element = node.childNodes[b];
					element_id = element.getAttribute("id");
					classname = element.getAttribute("classname");
					addclass = element.getAttribute("addclass");
					removeclass = element.getAttribute("removeclass");
					destroy = element.getAttribute("destroy");
					restore = element.getAttribute("restore");
					
					var list_id_part = "listitem_";
					
					dom_element = $(element_id);
					
					if (element.childNodes[0] != null) {
						dom_element.innerHTML = element.childNodes[0].nodeValue;
					}
					
					if (dom_element == null && !restore) {
						alert("process_xml_standard() - element with ID '" + element_id + 
							"' not found");
					}
					
					if (destroy) {
						//check for a listitem object and uncheck associated checkbox first						
						if (element_id.substring(0, list_id_part.length) == list_id_part) {
							list_id = element_id.substr(list_id_part.length);
							
							if ($("chk_listitem_" + list_id) != null)
								set_chk(null, "chk_listitem_" + list_id, null, false);
						}
						
						destroy_element(element_id);
						
						if (element_id.substring(0, list_id_part.length) == list_id_part)
							reset_info_bar();
						
						continue;
					} else if (restore) {
						//restore an element
						restore_element(element_id, true);
						
						if (element_id.substring(0, list_id_part.length) == list_id_part)
							reset_info_bar();
							
						continue;
					}
					
					if (classname != null) {
						$dom_element.className = classname;
					}
					
					if (addclass != null) {
						add_class(dom_element, addclass);
					}
					
					if (removeclass != null) {
						remove_class(dom_element, removeclass);
					}
				}
				
				break;
			
			case "edit_form":
				//edit the specified form elements
				type = node.getAttribute("type");
				form_name = node.getAttribute("name");
				
				if (type == "wipe") {
					//wipe the contents and default the selections of every form element
					document.forms[form_name].reset();
				} else {
					//loop through the <form_element>s and perform specific actions
					for (b = 0; b < node.childNodes.length; b++) {
						form_element = node.childNodes[b];
						element_name = form_element.getAttribute("name");
						focus = form_element.getAttribute("focus");
						
						if (form_element.childNodes[0]) {
							document.forms[form_name].elements[element_name].value = 
								form_element.childNodes[0].nodeValue;
						} else {
							document.forms[form_name].elements[element_name].value = "";
						}
						
						if (focus == "focus") {
							document.forms[form_name].elements[element_name].focus();
						}
					}
				}
				break;
			
			case "msgbar":
				//display an msgbar message!(?)
				var html = node.childNodes[0].nodeValue;
				var id = node.getAttribute("id");
				var duration = node.getAttribute("duration");
				var after = node.getAttribute("after");
				var style = node.getAttribute("style");
				
				if (msgbar = $(id)) {
					anim_scroll(0, 0);
					element_display(msgbar, "block");
					element_opacity(msgbar, 100);
					
					remove_all_classes(msgbar, "msgbar");
					
					if (style == "warning") {
						add_class(msgbar, "showWarning");
					} else if (style == "info") {
						add_class(msgbar, "showInfo");
					} else {
						add_class(msgbar, "showInactive");
					}
					
					var msg_span = document.createElement("p");
					msg_span.innerHTML = html;
					
					var old_message = msgbar.childNodes[0];
					
					msgbar.removeChild(msgbar.firstChild);
					
					msgbar.appendChild(msg_span);
					
						
					if (after == "revert" && duration != 0) {
						window.setTimeout(function() { 
							msgbar.removeChild(msgbar.firstChild);
							msgbar.appendChild(old_message);
						}, (duration * 1000));
					} else if (after == "remove" && duration != 0) {
						timed_fade_element(msgbar, duration);
					}
				}
				break;
				
			case "reload_datalist":
				//reload the current dynamic datalist
				var action = node.getAttribute("action");
				var post_data = node.getAttribute("postdata");
				var highlight = node.getAttribute("highlight");
				
				get_data_list(null, null, action, null, post_data, highlight);
				
				break;
			
			case "highlighter":
				//highlight listed nodes with an animation
				for (b = 0; b < node.childNodes.length; b++) {
					id = node.childNodes[b].getAttribute("id");
					highlight_element($(id));
				}
				
				break;
				
			case "breadcrumb":
				//produce a breadcrumb list
				var crumbs = Array();
				
				for (b = 0; b < node.childNodes.length; b++) {				
					crumb = Array();
					crumb['href'] = node.childNodes[b].getAttribute("href");
					crumb['label'] = node.childNodes[b].childNodes[0].nodeValue;
					
					onclick = node.childNodes[b].getAttribute("onclick");
					
					if (onclick != null) {
						crumb['onclick'] = new Function(onclick);
					}
					
					crumbs.push(crumb);
				}
				
				var parent_node = $(node.getAttribute("node"));
				
				if (parent_node != null)
					new breadcrumb(parent_node, crumbs);
				break;
			
			case "required_fields":
				//highlight labels matching the ID of required fields
				fields = node.getElementsByTagName("field");
				
				//unhighlight all currently highlighted labels
				labels = $a("label", "class", "required");
				
				for (b = 0; b < labels.length; b++) {
					remove_class(labels[b], "required");
				}				
				
				for (b = 0; b < fields.length; b++) {
					labels = $a("label", "for", fields[b].getAttribute("id"));
					
					if (labels.length > 0)
						add_class(labels[0], "required");
				}
				
				break;
			
			case "typecombo":
				//typeable combo list population
				element = $(node.getAttribute("element"));
				var tc_item;
				var tc_items = new Array();
				
				if (element != null) {
					//create data array
					for (b = 0; b < node.childNodes.length; b++) {
						tc_item = Array();
						tc_item['value'] = node.childNodes[b].getAttribute("value");
						tc_item['title'] = node.childNodes[b].getAttribute("label");
						
						if (node.childNodes[b].childNodes[0] != null)
							tc_item['desc'] = node.childNodes[b].childNodes[0].nodeValue;
						
						tc_items.push(tc_item);
					}
					
					new type_combo(element, tc_items);
				}
				
				break;
		}
		
		if (node.getAttribute("last") != null)
			return false;
	}
	
	//find commands that need to be executed after those previously found
	for (a = 0; a < xmldoc.childNodes.length; a++) {
		//loop through all sub-nodes of <root>
		node = xmldoc.childNodes[a];
		
		//alert("second set: node " + a + " of " + xmldoc.childNodes.length + ": " + node.tagName);
		
		switch (node.tagName) {
			case "data_list_stats":
				//stats being returned from a data list re(load)
				var page = node.getAttribute("page");
				var num_pages = node.getAttribute("num_pages");
				//var list_type = node.getAttribute("list_type");
				
				reset_info_bar();
				
				update_pager(num_pages, page, DL_ACTION, DL_POSTDATA, true, DL_LIST_TYPE);
				
				break;
		}
		
		if (node.getAttribute("last") != null)
			return false;
	}
}

function find_xml_element(response_xml, name, element_name) {
	//post_log("looking for element: " + element_name + " in a " + name + " tag");
	var elements, sub_elements;

	if (elements = response_xml.getElementsByTagName(name)) {

		for (var i = 0; i < elements.length; i++) {
			//loop through every element
			if (elements[i].getElementsByTagName(element_name).length > 0) {
				//tag has been found within the parent element
				sub_elements = elements[i].getElementsByTagName(element_name)
				if (sub_elements[0].hasChildNodes()) {
					return sub_elements[0].childNodes[0].nodeValue;
				} else {
					return "";
				}
			} else {
				return false;
			}
		}
	}
	//if the element isn't found, return false
	return false;
}

function create_request_str(form_name, mpart_files_sent) {
	
	var query = ""
	
	if (document.forms[form_name] != null) {
		var form = document.forms[form_name];
	} else {
		alert("Form '" + form_name + "' does not exist.");
	}

	for (a = 0; a < form.length; a++) {
		if (form.elements[a].disabled || form.elements[a].name == "") {
			//skip disabled and unnamed elements as per standard HTTP post requirements
			continue;
		}
		
		if (form.elements[a].tagName == "INPUT") {
			if (form.elements[a].type == "text"
				|| form.elements[a].type == "submit"
				|| form.elements[a].type == "button"
				|| form.elements[a].type == "hidden"
				|| form.elements[a].type == "password") {
				if (a > 0) query += "&";
				query += form.elements[a].name.urlencode() + 
					"=" + form.elements[a].value.urlencode();
			}
			
			if (form.elements[a].type == "checkbox") {
				if (form.elements[a].checked) {
					if (a > 0) query += "&";
					query += form.elements[a].name.urlencode() + 
						"=" + form.elements[a].value.urlencode();
				}
			}
			
			if (form.elements[a].type == "radio") {
				if (form.elements[a].checked) {
					if (a > 0) query += "&";
					query += form.elements[a].name.urlencode() + 
						"=" + form.elements[a].value.urlencode();
				}
			}
			
		}
		
		if (form.elements[a].tagName == "SELECT") {
			var sel = form.elements[a];
			var select_name;
			
			if (sel.multiple) {
				//multiple choice select
				sel_query = "";
				
				if (sel.options.length > 0) {
					//check select name for existing array definition
					if (sel.name.lastIndexOf("[]") == sel.name.length-2) {
						select_name = sel.name.substring(0, sel.name.length - 2);
					} else {
						select_name = sel.name
					}
					
					if (a > 0) query += "&";
					for (b = 0; b < sel.options.length; b++) {
						if (sel.options[b].selected && !sel.options[b].disabled) {
							if (b > 0 && sel_query != "") {
								sel_query += "&" + select_name.urlencode() + "[]=";
							}
							
							sel_query += sel.options[b].value.urlencode();
						}
					}
				
					query += select_name.urlencode() + "[]=" + sel_query;
				}
			} else {
				//single choice select
				if (sel.options.length > 0 && sel.selectedIndex != -1) {
					if (a > 0) query += "&";
					query += sel.name.urlencode() + 
						"=" + sel.options[sel.selectedIndex].value.urlencode();
				}
			}
		}
		
		if (form.elements[a].tagName == "TEXTAREA") {
			var html;
			
			if (class_exists(form.elements[a], "rte")) {
				//textarea is part of an RTE. get RTE content first
				rte_update_textarea(form.elements[a].id);
		 	}
		
			if (a > 0) query += "&";
			query += form.elements[a].name.urlencode() + 
				"=" + form.elements[a].value.urlencode();
		}
	}
	
	return query;
}



function send_form(xmldoc, response, form_name, kill_id, action, response_num) {
	
	if (xmldoc == null) {
		if (!document.forms[form_name]) {
			alert("Form '" + form_name + "' does not exist.");
			return false;
		} else {
			if (action == null) {
				action = document.forms[form_name].action;
			}
		}
		
		var post_data = create_request_str(form_name);

		if (response_num != null) {
			//the post is coming from the confirmation form
			post_data += "&ctx_response=" + response_num;
		}
		
		if (kill_id != null) {
			destroy_element(kill_id, true, true);
			ctx_unshade();
		}
		
		ajax_send(null, arguments, action, post_data, "POST");
		
		return false;
	}
}

function form_response(form_name, response_num, kill_id) {
	//alias for send_form (just with less of those annoying parameters)
	send_form(null, null, form_name, kill_id, null, response_num);
}

function send_std_request(xmldoc, response, action, post_data) {
	//sends a standard AJAX request with no special actions
	if (xmldoc == null) {
		if (response != null) {
			//add response to the post data, if any
			if (post_data != "") {
				post_data = "&ctx_response=" + response;
			} else {
				post_data = "ctx_response=" + response;
			}
		}
		
		ajax_send(send_std_request, arguments, action, post_data);
		return false;
	}
}

function check_submit(form_name, action) {
	var post_data = create_request_str(form_name);
	post_data += "&check_required=1&form_name=" + form_name.urlencode();
	
	ajax_send(null, arguments, action, post_data);
}



function create_domain(xmldoc, response) {
	//show the domain creator form
	if (xmldoc == null) {
		ajax_send(create_domain, arguments, PATH_ROOT + "/admin/actions.php", "a=create_domain");
		return false;
	}
}

function switch_domain(xmldoc, response, domain_id) {
	//switches the currently active domain
	if (response != CTX_RESPONSE_POSITIVE) {
		new ctx_confirm(LCL_CNFM_SWITCH_DOMAIN, 
			[
				[LCL_CHANGE_DOMAIN, function() { switch_domain(null, CTX_RESPONSE_POSITIVE, domain_id); }],
				[LCL_CANCEL, function() { reset_domain_select(); }, true]
			]);
	} else {
		post_data = "a=switch_domain&domain_id=" + domain_id;
		ajax_send(null, arguments, PATH_ROOT + "/admin/actions.php", post_data);
		return false;
	}
}

function single_action(xmldoc, response, record_id, url, action, extra_post_data) {
	//perform an action on the selected single record.
	if (xmldoc == null) {
		//if (!isNaN(record_id)) {
			post_data = "a=" + action + "&id=" + record_id;
	
			if (extra_post_data != null)
				post_data += "&" + extra_post_data;
		
			ajax_send(single_action, arguments, PATH_ROOT + url, post_data);
		
		
		return false;
	}
}

function quick_action(url, action) {
	//sends an quick action (with limited javascript required) to the selected url.
	//this function does not callback, and does not allow specifying record IDs
	post_data = "a=" + action;
	ajax_send(null, arguments, PATH_ROOT + url, post_data);
	return false;
}

function multi_action(ids, action, url, extra_post_data) {
	
	var post_data = "m_act=" + action;	
	var ids = ids.split(",");
	var a;
	
	if (extra_post_data != null)
		post_data += "&" + extra_post_data;
	
	for (a = 0; a < ids.length; a++)
		post_data += "&chk_listitem[]=" + ids[a];
	
	ajax_send(null, arguments, PATH_ROOT + url, post_data);
}

function get_data_list(callback, reserved, action, page, post_data, list_type, list_node) {
	
	DL_ACTION = action;
	DL_POSTDATA = post_data;
	
	if (post_data != "" && post_data != null) {
		post_data += "&";
	} else {
		post_data = "";
	}
	
	//get the list type and store globally
	if (list_type == null) {
		//use (and store, if unset) the default list type (list)
		if (DL_LIST_TYPE != null) {
			post_data += "a=" + DL_LIST_TYPE;
		} else {
			post_data += "a=list";
			DL_LIST_TYPE = "list";
		}
	} else {
		//use and store altered list type
		post_data += "a=" + list_type;
		DL_LIST_TYPE = list_type;
	}
		
	if (page != null)
		post_data += "&page=" + page;
	
	if (list_node != null) {
		post_data += "&list_node=" + list_node;
		DL_LIST_NODE = list_node;
	} else if (DL_LIST_NODE != null) {
		post_data += "&list_node=" + DL_LIST_NODE;
	}

	ajax_send(callback, arguments, action, post_data);
	return false;
}

function switch_user(xmldoc, response, username, password) {
	
	if (xmldoc == null) {
		post_data = "a=switchuser";
		
		if (username != "" && password != "")
			post_data += "&auth=1&u=" + username + "&p=" + password;
		
		ajax_send(switch_user, arguments, PATH_ROOT + "/admin/actions.php", post_data);
		return false;
	}
}

function preview_page(xmldoc, response, rev_id) {
	if (xmldoc == null) {
		post_data = "&a=preview_page&rev_id=" + rev_id;
		ajax_send(preview_page, arguments, PATH_ROOT + "/admin/pages/actions.php", post_data);
		return false;
	}
}


function load_tip(xmldoc, response, tip_name, e) {
	//loads a tip with the ID of tip_id and displays it
	if (xmldoc != null) {
		//display the tip in a panel		
		tips = xmldoc.getElementsByTagName("tip");
		
		x = parseInt(tips[0].getAttribute("x"));
		y = parseInt(tips[0].getAttribute("y"));
		
		display_tip(tips[0].getAttribute("name"), tips[0].childNodes[0].nodeValue, x, y)
	} else {
		e = e || window.event();
		
		try {
			e.preventDefault();
		} catch(err) {
			e.returnValue = false;
		}
		
		var panel = $("tipPanel");
		var cursor = get_cursor_pos(e);
		var x = cursor[0];
		var y = cursor[1];
		
		if (panel != null) {
			if (panel.rel == tip_name)
				return false;
		} else if (DISPLAYED_TIPS[tip_name] != null) {
			display_tip(tip_name, DISPLAYED_TIPS[tip_name], x, y);
			return false;
		}
		
		post_data = "tip_name=" + tip_name + "&lang_id=" + LANG_ID +
		 	"&x=" + x + "&y=" + y;
		ajax_send(load_tip, arguments, PATH_ROOT + "/admin/tip.php", post_data);
			
		return false;
	}
}

function switch_dir(dir_id, list_type) {
	if (typeof(CURRENT_DIR_ID) != "undefined") {
		if (dir_id == CURRENT_DIR_ID)
			return false;
	}
		
	if (list_type == null)
		list_type = "list";
	
	if (!isNaN(dir_id))
		get_data_list(null, null, PATH_ROOT + "/admin/files/actions.php", null, 
			"dir_id=" + dir_id, list_type);
	
	if (typeof(CURRENT_DIR_ID) != "undefined")	
		CURRENT_DIR_ID = dir_id;
		
	return false;
}

function get_cat_path(xmldoc, response, callback, cat_id, page_id) {
	
	post_data = "a=cat_path";
	
	if (cat_id != null)
		post_data += "&cat_id=" + cat_id;
		
	if (page_id != null)
		post_data += "&page_id=" + page_id;
	
	ajax_send(callback, arguments, PATH_ROOT + "/admin/actions.php", post_daa);
	return false;
}

function update_catpage_select(xmldoc, response, cat_select_id, page_select_id) {
	
	if (xmldoc != null) {
		//update selects
		var pages = xmldoc.getElementsByTagName("page");
		var select_id = xmldoc.getElementsByTagName("select_id")[0].childNodes[0].nodeValue;
		select_id = $(select_id);
		
		wipe_select(select_id);
		
		if (pages.length > 0) {
			for (a = 0; a < pages.length; a++) {
				page_id = pages[a].getAttribute("id");
				add_select_value(select_id, pages[a].childNodes[0].nodeValue, page_id);
			}
		} else {
			add_select_value(select_id, "(" + LCL_NO_PAGES + ")", 0);
		}
	} else {
		post_data = "a=data_list";
		
		var rtelink_cat_id = $(cat_select_id);		
		var rtelink_page_id = $(page_select_id);
		
		if (rtelink_cat_id == null || rtelink_page_id == null)
			return false;
		
		var cat_id = rtelink_cat_id.options[rtelink_cat_id.selectedIndex].value.trim();
		
		//category is not selected, revert page selector to "Select a cat.."
		if (isNaN(cat_id) || cat_id == 0) {
			wipe_select(rtelink_page_id);
			add_select_value(rtelink_page_id, LCL_SELECT_CAT, 0);
			return false;
		}
		
		//wipe select, add "Loading.." message
		wipe_select(rtelink_page_id);
		add_select_value(rtelink_page_id, LCL_LOADING + "...", 0);
		
		post_data += "&cat_id=" + cat_id +
			"&select_id=" + page_select_id;
		
		ajax_send(update_catpage_select, arguments, 
			PATH_ROOT + "/admin/categories/actions.php", post_data);
		return false;
	}
}

function do_login(xmldoc, response, form_name) {
	if (xmldoc != null) {
		var login_count = xmldoc.getElementsByTagName("login_count");
		
		if (login_count.length > 0) {
			var counter = parseInt(login_count[0].childNodes[0].nodeValue);
			if (counter >= MAX_BASIC_LOGINS) {
				//display/update captcha image, as it's now required
				var img = $("captcha_img");
				var shell = $("div_captcha");
				
				if (img != null && shell != null) {
					var d = new Date();
					img.src = PATH_ROOT + "/modules/cortex/captcha.php?" + d.getMilliseconds();
					shell.style.display = "block";
				}
			}
		}
	} else {
		var post_data = create_request_str(form_name);

		if (response != null) {
			post_data += "&ctx_response=" + response;
		}
		
		ajax_send(do_login, arguments, PATH_ROOT + "/admin/actions.php", post_data);
		
		return false;
	}
}

function login_action(xmldoc, response, form_name) {
	var post_data = create_request_str(form_name);
	ajax_send(null, arguments, PATH_ROOT + "/admin/actions.php", post_data);
	return false;
}

function add_user_group(xmldoc, response, e) {
	
	if (xmldoc != null) {
		//add group to the container and select it
		var groups = xmldoc.getElementsByTagName("user_group");
		var container = $("user_group_list");
		
		for (a = 0; a < groups.length; a++) {
			//create group checkbox :)
			var shell = document.createElement("DIV");
			shell.className = "editFormElement";
			
			var input = document.createElement("INPUT");
			input.type = "checkbox";
			input.name = "groups[]";
			input.id = "chk_group_" + groups[a].getAttribute("id");
			input.value = groups[a].getAttribute("id");
			input.checked = true;
			
			var label = document.createElement("label");
			label.setAttribute("for", input.id);
			label.htmlFor = input.id;
			label.style.backgroundColor = "#ffc";
			label.innerHTML = "&nbsp;" + groups[a].childNodes[0].nodeValue;
			
			container.appendChild(shell);
			shell.appendChild(input);
			shell.appendChild(label);
		}
	} else {
		var group_name = $("txt_new_group_name");
		
		if (group_name.value.trim() == "") {
			e = e || window.event();
			load_tip(null, null, "add_group_noname", e);
			return false;
		}
		
		group_name_val = group_name.value.trim();
		group_name_val = group_name_val.urlencode();
		
		var post_data = "a=quick_add&name=" + group_name_val;
		
		ajax_send(add_user_group, arguments, PATH_ROOT + "/admin/manage/groups/actions.php", post_data);
		
		group_name.value = "";
		
		return false;
	}	
}

function preview_file(e, file_id) {
	//previews a file
	e = e||window.event();
	
	ajax_send(null, arguments, PATH_ROOT + "/admin/files/actions.php", "a=show_preview_file&id=" + file_id);
		
	if (window.event) {
		window.event.cancelBubble = true;
	} else if (e != null) {
		e.stopPropagation();
	}
	
	return false;
}

function update_usercombo(xmldoc, response, element, extra_post_data, search) {
	
	if (xmldoc != null) {
		var userlist = Array();
		var a, full_name, user;
		
		xml_users = xmldoc.getElementsByTagName("user");
		
		if (xml_users.length > 0) {
			for (a = 0; a < xml_users.length; a++) {
				user = Array();
				
				
				username = xml_users[a].childNodes[0].childNodes[0].nodeValue;
				full_name = xml_users[a].childNodes[1].childNodes[0].nodeValue + " " + 
					xml_users[a].childNodes[2].childNodes[0].nodeValue;
				
				user['value'] = username;
				user['title'] = username + " (" + full_name + ")";
				
				userlist.push(user);
			}
		}
		
		USER_TYPECOMBO.update_data(userlist);
	} else {
		if (!search) {
			//clear old timeout
			if (TIMERS['update_usercombo'] != null)
				window.clearTimeout(TIMERS['update_usercombo']);
				
			//begin timeout for checking username
			TIMERS['update_usercombo'] = window.setTimeout(function() {
				update_usercombo(null, null, element, extra_post_data, true);
			}, 200);
		} else {
			//send AJAX request			
			if (element != null) {
				post_data = "a=xml_users&query=" + element.value.urlencode();
				
				if (extra_post_data != null)
					post_data += "&" + extra_post_data;
				
				ajax_send(update_usercombo, arguments, PATH_ROOT + "/admin/manage/users/actions.php", post_data);
			} else {
				alert("update_usercombo() - typecombo element not found");
			}
		}
	}
}

function load_cat_tpl_keys(xmldoc, response) {
	
	if (xmldoc != null) {
		//add template keys
		xml_keys = xmldoc.getElementsByTagName("key");
		key_list = $("div_key_list");
		
		//clear template key list
		key_list.innerHTML = "";
		var link, id;
		
		for (a = 0; a < xml_keys.length; a++) {
			//for each key, add to the template key list
			link = xml_keys[a].getAttribute("link");
			id = xml_keys[a].getAttribute("id");
			label = xml_keys[a].getAttribute("label");
			
			key = document.createElement("A");
			key.href = "#";
			key.innerHTML = label;
			
			if (link != null) {
				key.rel = link;
			}
			
			key_val = "{$" + id + "}";
			
			attach_event(key, "click", func_event(insert_cat_tpl_key, key_val, true));
			
			key_list.appendChild(key);
		}
		
		//add sort fields
		load_cat_list_fields(xmldoc, response);
		
	} else {
		var post_data = "a=load_template_keys&id=" + CURRENT_CAT_ID;
		
		ajax_send(load_cat_tpl_keys, arguments, PATH_ROOT + "/admin/categories/actions.php", post_data);
		return false;
	}
}

function load_cat_list_fields(xmldoc, response) {	
	
	if (xmldoc != null) {
		xml_keys = xmldoc.getElementsByTagName("key");
		var keys = new Array();
		
		//get order fields
		var sel_order_fields = $("sel_order_fields");
		
		//clear order field selector
		if (sel_order_fields != null)
			wipe_select(sel_order_fields);
		
		for (a = 0; a < xml_keys.length; a++) {
			//add each key to the order field selector
			id = xml_keys[a].getAttribute("id");
			label = xml_keys[a].getAttribute("label");
			
			keys.push(id);
			
			//order field selector
			if (sel_order_fields != null
				&& id != "page_url"
				&& id != "body_abstract")
				add_select_value(sel_order_fields, label, id);
		}
		
		//wipe ordered fields that don't exist in the new key array
		if (sel_order_fields != null) {
			var root_node = $("div_orderelements");
			var element_order_fields = $a("input", "rel", "element_order_name", root_node);
			
			if (element_order_fields.length > 0) {
				for (a = 0; a < element_order_fields.length; a++) {
					if (!in_array(element_order_fields[a].value, keys))
						destroy_element("order_field_" + element_order_fields[a].value);
				}
			}
		}
	} else {
		var post_data = "a=load_template_keys&id=" + CURRENT_CAT_ID;
		
		ajax_send(load_cat_list_fields, arguments, PATH_ROOT + "/admin/categories/actions.php", post_data);
		return false;
	}
}

function load_form_tpl_keys(xmldoc, response) {
	if (xmldoc != null) {		
		//add template keys
		keys = xmldoc.getElementsByTagName("key");
		
		key_list = $("div_key_list");
		key_list.innerHTML = "";
		
		var id, tc_key;
		
		for (a = 0; a < keys.length; a++) {
			label = keys[a].getAttribute("label");
			id = keys[a].getAttribute("id");
			type = parseInt(keys[a].getAttribute("type"));
			
			key = document.createElement("A");
			key.href = "#";
			key.innerHTML = label;
			
			var key_val = "{field:" + id + "}";
			
			//fill template key typeable combo array
			if (type == 2) {
				tc_key = new Array();
				tc_key['value'] = key_val;
				tc_key['title'] = label;
				tc_key['desc'] = keys[a].textContent;
				
				TC_KEYS.push(tc_key);
			}
			
			
			//attach_event(key, "click", function() { insert_template_key(event, key_val); });
			attach_event(key, "click", func_event(insert_form_tpl_key, key_val, true));
			
			key_list.appendChild(key);
		}
		
		//initialise typeable combo objects
		type_combo_init($("recipientsPanel"), TC_KEYS);
		
		new type_combo($("email_from"), TC_KEYS);
	} else {
		var post_data = "a=load_template_keys&form_id=" + FORM_ID;
		
		ajax_send(load_form_tpl_keys, arguments,
			PATH_ROOT + "/admin/forms/responders/actions.php", post_data);
		return false;
	}
}

function send_file(xmldoc, response) {
	//sends a file to the server using the upload form
	var form = document.forms['intform_edit_file'];

	if (xmldoc != null) {
		//send upload
		precheck_result = xmldoc.getElementsByTagName("precheck_result")[0];
		
		if (precheck_result != null) {
			if (precheck_result.childNodes[0].nodeValue != "false") {				
				//send form
				form.setAttribute("enctype", "multipart/form-data");
				form.onsubmit = null;			
				
				//disable buttons
				var buttons = $("div_intform_buttons");
				
				for (var a = 0; a < buttons.childNodes.length; a++) {
					if (buttons.childNodes[a].tagName == "INPUT")
						buttons.childNodes[a].disabled = true;
				}
				
				form.submit();
				
				//show loading animation
				add_class(buttons, "buttonLoading");
			}
		}
		
		return false;
	} else {
		//send precheck data
		form.setAttribute("enctype", "appliation/x-www-form-urlencoded");
		
		var post_data = "a=" + form.elements['a'].value +
			"&precheck=" + form.elements['upload_file'].value.urlencode();
			
		if (form.elements['parent_id'] != null)
			post_data += "&parent_id=" +
			form.elements['parent_id'].options[form.elements['parent_id'].selectedIndex].value;
			
		if (form.elements['id'] != null)
			post_data += "&id=" + form.elements['id'].value;
			
		if (form.elements['popup'] != null)
			post_data += "&popup=1";
			
		if (form.elements['file_label'] != null)
			post_data += "&file_label=" + form.elements['file_label'].value.urlencode();
			
		ajax_send(send_file, arguments, PATH_ROOT + "/admin/files/actions.php", post_data);
		return false;
	}
}

var ANIM_SCROLL_TARGET_X = 0;
var ANIM_SCROLL_TARGET_Y = 0;

function timed_fade_element(element, wait) {
	//starts the fading and removal of an element, after the specified wait in seconds
	window.setTimeout(function() { fade_element(element) }, parseInt(wait * 1000));
}

function reveal_element(element, display_type, speed, target_opacity, opacity) {
	//slowly reveals an element into display by way of a fade
	if (speed == null) speed = 5;
	if (target_opacity == null) target_opacity = 100;
	
	step = 10;
	
	if (opacity != null) {
		opacity += step;
		
		if (opacity >= target_opacity || (opacity + step) > target_opacity) {
			element_opacity(element, target_opacity);
			return;
		} else {
			element_opacity(element, opacity);
		}
	} else {
		element_opacity(element, 0);
		element.style.display = display_type;
		var opacity = 0;
	}
	
	window.setTimeout(function() { reveal_element(element, null, speed, target_opacity, opacity) }, speed);
}

function fade_element(element, speed, destroy_after, opacity) {
	//fades and then removes an element from display
	if (speed == null) speed = 5;
	
	if (opacity != null) {
		opacity -= 10;
		
		if (opacity == 0) {
			if (destroy_after) {
				return element.parentNode.removeChild(element);
			} else {
				element.style.display = "none";
				return null;
			}
		} else {
			element_opacity(element, opacity);
			element.style.overflow = "hidden";
		}		
	} else {
		opacity = get_element_opacity(element);
	}
	
	window.setTimeout(function() { fade_element(element, speed, destroy_after, opacity) }, speed);
}

function slidein_element(element, display_type, direction, target_top, top, opacity) {
	//reveals an element into display by way of a fade and sliding motion	
	if (top != null && opacity != null) {
		if (direction == null || direction == "down") {
			top += 1;
		} else {
			top -= 1;
		}
		
		opacity += 10;
		
		if (direction == null || direction == "down") {
			if (element.offsetTop >= target_top) {
				element_opacity(element, 100);
				element.style.top = target_top + "px";
				return;
			} else {
				element_opacity(element, opacity);
				element.style.top = top + "px";
			}
		} else {
			if (element.offsetTop < target_top) {
				element_opacity(element, 100);
				element.style.top = target_top + "px";
				return;
			} else {
				element_opacity(element, opacity);
				element.style.top = top + "px";
			}
		}
	} else {
		var opacity = 0;
		element_opacity(element, opacity);
		element_display(element, display_type);
		
		element.style.visibility = "visible";

		//target is to move 10 pixels down
		target_top = element.offsetTop;
		
		if (direction == null || direction == "down") {
			//shift 10 pixels down
			var top = (element.offsetTop - 10);
		} else {
			//shift 10 pixels up
			var top = (element.offsetTop + 10);
		}
		
		element.style.top = top + "px";
	}
	
	window.setTimeout(function() { slidein_element(element, null, direction, target_top, top, opacity) }, 2);
}

function slideout_element(element, direction, opacity, orig_top) {
	//opposite of slidein_element
	step = 20;
	
	if (opacity != null) {
		opacity -= step;
		
		if (opacity == 0 || (opacity - step) == 0) {
			element_display(element, "none");
			element.style.top = orig_top + "px";
			return;
		} else {
			element_opacity(element, opacity);
			element.style.overflow = "hidden";
			
			if (direction == null || direction == "up") {
				//slide up
				element.style.top = (element.offsetTop - 1) + "px";
			} else {
				//slide down
				element.style.top = (element.offsetTop + 1) + "px";
			}
		}		
	} else {
		opacity = get_element_opacity(element);
		orig_top = element.offsetTop;
	}
	
	window.setTimeout(function() { slideout_element(element, direction, opacity, orig_top) }, 3);
}

function highlight_element(element, highlight, count) {
	if (highlight == null) highlight = true;
	if (count == null) count = 1;
	
	if (highlight && count < 4) {
		add_class(element, "highlightedObj");
	} else {
		remove_class(element, "highlightedObj");
	}
	
	if (count < 4) {
		window.setTimeout(function() { highlight_element(element, !highlight, ++count) }, 500);		
	}
}

function element_opacity(element, opacity) {
	
	element.style.opacity = (opacity / 100);
	element.style.filter = 'alpha(opacity=' + opacity + ')';
}

function get_element_opacity(element) {
	if (element.style.opacity) {
		return (element.style.opacity * 100);
	} else if (element.style.filter) {
		var match = element.style.filter.match(/alpha\(opacity=(.*?)\)/i);
		if (match.length == 2) {
			return match[1];
		} else {
			return 100;
		}
	} else {
		return 100;
	}
}

function anim_scroll(x, y) {
	if (!window.scrollTo) return;
	window.scrollTo(x, y);
}

function init_dyntbl_column(e, column, cat_id) {
	var e = e||window.event;
	var table_id = dyntbl_find_id(column);
	
	if (table_id != null) {
		if (DYNTABLE_SELECTORS["dyntable_" + table_id] != null) {
			DYNTABLE_SELECTORS["dyntable_" + table_id].show(e, column);
		} else {
			DYNTABLE_SELECTORS["dyntable_" + table_id] = new tbl_column_selector(e, table_id, cat_id);
			DYNTABLE_SELECTORS["dyntable_" + table_id].show(e, column);
		}
	}
	
	try {
		e.preventDefault();
	} catch(err) {
		e.returnValue = false;
	}
}

function tbl_column_selector(e, table_id, cat_id) {
	this.table = $("dyntable_" + table_id);
	
	if (this.table == null)
		return false;
	
	//category ID
	this.cat_id = cat_id;
	
	//table ID
	this.table_id = table_id;
	this.panel_id = "table_" + this.table_id + "_selector";;
	
	//create sorter
	this.sorter_cache = null;
	
	//body event added
	this.body_event = false;
}

tbl_column_selector.prototype.show = function(e, column) {
	if (column != null)
		this.column = column;
	
	if ($(this.panel_id) == null) {
		//create options panel
		this.panel = create_element(
				"abs_container",
				"DIV",
				this.panel_id,
				"popupSelect"
			);
		
		this.panel_form = document.createElement("FORM");
		this.panel_form.action = "#";
		this.panel_form.method = "post";
		this.panel.appendChild(this.panel_form);
		
		this.sorter = document.createElement("SELECT");
		this.panel_form.appendChild(this.sorter);
		
		//fill the sorter with delicious data
		this.fill_sorter();
		
		//insert buttons
		var button_container = document.createElement("DIV");
		button_container.className = "buttons";
		this.panel_form.appendChild(button_container);
		
		this.cancel_btn = document.createElement("INPUT");
		this.cancel_btn.type = "button";
		this.cancel_btn.value = "Close";
		attach_event(this.cancel_btn, "click", obj_event(this, "hide", null));
		button_container.appendChild(this.cancel_btn);
		
		this.apply_btn = document.createElement("INPUT");
		this.apply_btn.type = "button";
		this.apply_btn.value = "Apply";
		attach_event(this.apply_btn, "click", obj_event(this, "update_column", this.column));
		button_container.appendChild(this.apply_btn);
		
		//add event to panel to cancel out body click
		attach_event(this.panel, "click", obj_event(this, "cancel_click", null, true));
	}
	
	//get coordinates of callee column
	var col_top = element_top_pos(this.column);
	var col_left = element_left_pos(this.column);
	
	//get offset coordinates based on panel container
	var top = (col_top - element_top_pos(this.panel.parentNode)) - 5;
	var left = (col_left - element_left_pos(this.panel.parentNode))  - 5;
	
	//position panel
	this.panel.style.top = top + "px";
	this.panel.style.left = left + "px";
	
	//hey presto!
	remove_class(this.panel, "hidden");
	
	//cancel bubbling (or the body event we just added would fire!)
	if (window.event) {
		window.event.cancelBubble = true;
	} else if (e != null) {
		e.stopPropagation();
	}
	
	//add event to body to remove this item
	if (this.body_event == false) {
		attach_event(document.body, "click", obj_event(this, "hide", null));
		this.body_event = true;
	}
}

tbl_column_selector.prototype.fill_sorter = function(xmldoc, response) {
	if (xmldoc != null) {
		//fill ths sort field selector with data
		var cols = xmldoc.getElementsByTagName("tbl_col");
		
		wipe_select(this.sorter);
		
		//get database column ID from xhtml entity ID
		var col_id = this.column.id.substr(4);
		
		for (a = 0; a < cols.length; a++) {
			add_select_value(
				this.sorter, 
				cols[a].childNodes[0].nodeValue, 
				cols[a].getAttribute("id"), 
				null, 
				false
			);
		}
		
		//select column currently chosen
		select_val(this.sorter, col_id);
		
		//create cache
		if (this.sorter_cache == null)
			this.sorter_cache = xmldoc;
	} else {
		if (this.sorter_cache == null) {
			//get data from XML
			add_select_value(this.sorter, LCL_LOADING + "...", 0, null, false);
				
			var post_data = "a=get_tbl_cols&table_id=" + this.table_id +
				"&ignore_shown=1&column_id=" + this.column.id.substr(4);
			ajax_send(
					obj_event(this, "fill_sorter"),
					arguments,
					PATH_ROOT + "/actions.php",
					post_data
				);
		} else {
			//fill from cache
			this.fill_sorter(this.sorter_cache);
		}
	}
}

tbl_column_selector.prototype.cancel_click = function(e, event_obj, args) {
	if (window.event) {
		window.event.cancelBubble = true;
	} else if (e != null) {
		e.stopPropagation();
	}
}

tbl_column_selector.prototype.hide = function(e, event_obj, args) {
	//remove event from body
	detach_event(document.body, "click", obj_event(this, "hide", null));
	this.body_event = false;
	
	//remove item
	if ($(this.panel_id) != null)
		destroy_element(this.panel);
}

tbl_column_selector.prototype.update_column = function(e, event_obj, column) {
	//get database column ID from xhtml entity ID
	var col_id = this.column.id.substr(4);
	
	//get selected ID
	var new_col_id = this.sorter.options[this.sorter.selectedIndex].value;
	
	var post_data = "a=upd_tbl_col&table_id=" + this.table_id + 
		"&col_id=" + col_id + "&new_col_id=" + new_col_id;
	
	ajax_send(null, arguments, PATH_ROOT + "/actions.php", post_data);
	this.hide();
}

function dyntbl_find_id(element) {
	//get table ID
	var table_id = null;
	var parent = element.offsetParent;
	
	while (parent.offsetParent) {
		if (parent.tagName == "TABLE") {
			table_id = parent.id;
			table_id = table_id.replace(/dyntable_/, "");
			break;
		}
		
		parent = parent.offsetParent;
	}
	
	return table_id;
}

function dyntbl_switch_order(column) {
	//sends an xml request to switch order of a dynamic table
	var table_id = dyntbl_find_id(column);
	
	var column_id = column.id.replace(/dtc_/, "");
	
	if (table_id != null) {
		var post_data = "a=sort_tbl_col&table_id=" + table_id + "&column_id=" + column_id;
		ajax_send(null, arguments, PATH_ROOT + "/actions.php", post_data);
	}
	
	return false;
}