// redirect if we're not using a secure connection (in case the user forgot to specify https as the protocol)
// this can also be used to force a particular subdomain, in this case secure

	function require_SSL() {
		if (document.URL.indexOf("https://secure.") == -1) { 
			document.write("<p class=subheading>Redirecting to a secure port...</p>");
			location = "https://secure." + document.URL.substr(document.URL.indexOf("typea.net"), document.URL.length);
		}
	}


// redirect if we no longer need a secure connection

	function avoid_SSL() {
		if (document.URL.indexOf("https://") != -1) { 
			document.write("<p class=subheading>Redirecting to a standard port...</p>")
			// use the same subdomain we're on right now; just change the protocol
			location = "http://www." + document.URL.substr(document.URL.indexOf("typea.net"), document.URL.length);
		}
	}


// reload into the top window if someone put us into a frame

	function escape_from_frames() {
		if (self != top) {
			top.location = self.location.href;
		}
	}
	

// show a warning message to users of unsupported browsers

	function check_browser_support() {
		//alert(navigator.appVersion); return; // test user agent strings
		unsupported = new Array("MSIE 6");
		supported = 1;
		warned = 1;
		for (var i=0; i<unsupported.length; i++) {
			if (navigator.appVersion.indexOf(unsupported[i]) != -1) {
				supported = 0;
			}
		}
		if (!supported) {
			warned = get_cookie("browser_support", "warned");
		}
		if ((!supported)&&(!warned)) {
			alert("Your browser is not supported by this website. Please consider upgrading to a newer browser. Otherwise, portions of this site may not display or function correctly.");
			set_cookie("browser_support", "warned", 1);
		}
	}


// load a different stylesheet for Mac or Windows
// in your content file, just use a hard link to the platform that the files will most often be edited on
// - you can set it to styles_mac.css during development, then change to styles_win.css at delivery
// - since this runs on every page load, we've commented it out here; comment it back in if you need it
// - Windows conditional statements are preferred if available; see multiple_stylesheets.html for an example

	//platform = (navigator.platform.indexOf("Win") == -1) ? "mac" : "win" ;
	//document.write("<link rel=stylesheet href=../content/styles_" + platform + ".css type=text/css>");


// construct Flash object and embed tags
// FlashVars should be a string of name/value pairs separated by ampersands
// NOTE: we're using a PHP version of this function now

	function get_flash_tags(filename, width, height, bgcolor, flashvars) {
		name = filename;
		if (name.indexOf("/")) { name = name.substring(name.lastIndexOf("/") + 1); }
		if (name.indexOf(".")) { name = name.substring(0, name.indexOf(".")); }
		
		output = '<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
		output += ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"';
		output += ' WIDTH="' + String(width) + '" HEIGHT="' + String(height) + '" id="' + name + '" name="' + name + '" ALIGN="">';
		output += '<PARAM NAME=movie VALUE="' + filename + '"> <PARAM NAME=quality VALUE=high> <PARAM NAME=bgcolor VALUE=' + bgcolor + '> <EMBED src="' + filename + '" quality=high bgcolor=' + bgcolor + ' WIDTH="' + String(width) + '" HEIGHT="' + String(height) + '" NAME="' + name + '" ALIGN=""';
		output += ' TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer" swLiveConnect="true"';
		output += ' FlashVars="' + flashvars + '"></EMBED>';
		output += '<PARAM NAME="FlashVars" VALUE="' + flashvars + '">';
		output += '</OBJECT>';
		return output;
	}


// try to do a LiveConnect command, or show an alert if that's not available	
	
	function try_live_connect(command) {
		if (((navigator.appName.indexOf("Microsoft") != -1)&&(navigator.platform.indexOf("Mac") != -1)) || (navigator.appVersion.indexOf("Safari") != -1)) {
			alert("We're sorry, that feature is not available in this browser.")
		} else {
			eval(command);
		}
	}
	

// set a cookie
	
	// 02/15/03 - initial development
	// 05/14/04 - expanded to allow saving multiple values within a single cookie

	function set_cookie_OLD(name, value) {
		// specify a default key-name prefix for this project
		name_prefix = "";
		
		// the following are cookie parameters are optional ... change values to null if you don't need them
		hours = 43800;
		path = "/";						
		domain = null;
		
		key = name_prefix + name;		
		(hours) ? expiration_date = new Date((new Date()).getTime() + hours*3600000).toGMTString() : expiration_date = false;
		cookie_string = key + '=' + escape(value) + ((expiration_date)?(';expires=' +expiration_date):'') + ((path)?(';path='+path):'') + ((domain)?(';domain='+domain):'');
		document.cookie = cookie_string;
	}
	
	function set_cookie(name, key, value) {

		// specify a default cookie name prefix for this project, to group cookies together
		name_prefix = "";
		value_exists = 0;
		
		// the following cookie parameters are optional ... change values to null if you don't need them
		hours = 43800;
		path = "/";						
		domain = null;
		
		cookie_value = get_cookie(name, "all"); // get the entire cookie string so we can update the values
		key_value_array = cookie_value.split("&"); // split the cookie string into an array of key=value strings
		
		// search through the list of key=value strings for the presence of the specified key
		for (key_value=0; key_value<key_value_array.length; key_value++) {
			if (key_value_array[key_value].indexOf(key + "=") == 0) {
			
				// when we find it, overwrite the value of that array element
				key_value_pair = key_value_array[key_value].split("=");
				key_value_key = key_value_pair[0];
				key_value_array[key_value] = key_value_key + "=" + escape(value);
				
				// then combine the key=value strings back into a single string
				cookie_value = key_value_array.join('&');
				value_exists = 1; // flag this key as already being in the cookie string so that you don't add it to the cookie string again
				break;

			}
		}
		
		// if the cookie string doesn't already contain this key, add the key=value string
		if (value_exists == 0) {
			ampersand = (cookie_value != "") ? "&" : "" ;
			cookie_value += ampersand + key + "=" + escape(value);
		}
		
		expiration_date = (hours) ? new Date((new Date()).getTime() + hours*3600000).toGMTString() : false ;
		cookie_string = name_prefix + name + "=" + escape(cookie_value) + ((expiration_date)?(';expires=' +expiration_date):'') + ((path)?(';path='+path):'') + ((domain)?(';domain='+domain):'');
		document.cookie = cookie_string;
	
	}


// get values from a cookie

	// 02/15/03 - initial development
	// 05/14/04 - expanded to allow saving multiple values within a single cookie

	function get_cookie_OLD(name) {
		var output_value;
		if (document.cookie != "") {
			// specify a default key-name prefix for this project
			name_prefix = "";
			cookie_array = document.cookie.split("; ");
			for (n = 0; n < cookie_array.length; n++) {
				cookie_pair = cookie_array[n].split("=");
				key = cookie_pair[0];
				value = cookie_pair[1];
						
				if (key == (name_prefix + name)) {
					output_value = unescape(value);
					break;
				} else {
					output_value = "";
				}
			}
		} else {
			output_value = "";
		}
		return output_value;
	}
	
	function get_cookie(name, key) {
	
		name_prefix = ""; // specify a default cookie name prefix for this project, to group cookies together
		output_value = "";
		
		if (document.cookie != "") {
			cookie_array = document.cookie.split("; ");

			// search for the cookie with this name
			for (cookie=0; cookie < cookie_array.length; cookie++) {
				if (cookie_array[cookie].indexOf(name_prefix + name + "=") == 0) {

					cookie_pair = cookie_array[cookie].split("=");
					cookie_value = unescape(cookie_pair[1]); // the value is the list of key=value strings that we saved into this cookie
	
					if (key == "all") {
						output_value = cookie_value;
						break;
					} else {
						key_value_array = cookie_value.split("&");
					
						// search through the list of key=value strings for the value of the requested key
						for (key_value=0; key_value<key_value_array.length; key_value++) {
							if (key_value_array[key_value].indexOf(key + "=") == 0) {
							
								key_value_pair = key_value_array[key_value].split("=");
								output_value = unescape(key_value_pair[1]);
								break;
										
							}	
						}
						
					}
					
				}
			}
			
		}
		
		return output_value;
		
	}
	
	
// provide buttons for saving and loading form values with a cookie
// this is primarily used to streamline development and testing, but it could have real-world applications as well

	// 03/01/05 - initial development
	// 07/15/05 - added length check to checkbox and radio button groups to avoid an error when those groups only contain one element

	function show_form_cookie_buttons(form_name) {
		output = "";
		output += "<a href=\"JavaScript:form_cookie_save('" + form_name + "')\">Save form values</a><br>";
		output += "<a href=\"JavaScript:form_cookie_load('" + form_name + "')\">Load form values</a>";
		return output;
	}

	function form_cookie_save(form_name) {
		last_name = "";
		eval("this_form = document." + form_name);
		for (var i=0; i<this_form.length; i++) {
			value = "";
			if (this_form[i].type != "hidden") { // don't save hidden values
				if (this_form[i].name != last_name) { // don't process the checkbox and radio groups more than once
					if ((this_form[i].type == "text")||(this_form[i].type == "textarea")||(this_form[i].type == "password")) {
						value = this_form[i].value;
					} else if (this_form[i].type == "select-one") {
						value = this_form[i].selectedIndex;
					} else if (this_form[i].type == "select-multiple") {
						values = new Array();
						for (var j=0; j<this_form[j].options.length; j++) {
							if (this_form[i].options[j].selected) {
								values[values.length] = j;
							}
						}
						value = values.join(",");
					} else if (this_form[i].type == "checkbox") {
						eval("this_group = this_form." + this_form[i].name);
						if (this_group.length > 0) {
							values = new Array();
							for (j=0; j<this_group.length; j++) {
								if (this_group[j].checked) {
									values[values.length] = j;
								}
							}
							value = values.join(",");
						} else {
							value = "0";
						}
					} else if (this_form[i].type == "radio") {
						eval("this_group = this_form." + this_form[i].name);
						if (this_group.length > 0) {
							for (j=0; j<this_group.length; j++) {
								if (this_group[j].checked) {
									value = j;
									break;
								}
							}
						} else {
							value = "0";
						}
					}
					//alert(this_form[n].name + " = " + value);
					set_cookie(form_name, this_form[i].name, value);
					last_name = this_form[i].name;
				}
			}
		}
	}
	
	function form_cookie_load(form_name) {
		last_name = "";
		eval("this_form = document." + form_name);
		for (var i=0; i<this_form.length; i++) {
			value = "";
			if (this_form[i].type != "hidden") { // don't load hidden values
				if (this_form[i].name != last_name) { // don't process the checkbox and radio groups more than once
					value = get_cookie(form_name, this_form[i].name, value);
					//alert(this_form[i].name + " = " + value);
					if ((this_form[i].type == "text")||(this_form[i].type == "textarea")||(this_form[i].type == "password")) {
						this_form[i].value = value;
					} else if (this_form[i].type == "select-one") {
						this_form[i].selectedIndex = value;
					} else if (this_form[i].type == "select-multiple") {
						values = value.split(",");
						for (j=0; j<values.length; j++) {
							this_form[i].options[values[j]].selected = true;
						}
					} else if (this_form[i].type == "checkbox") {
						eval("this_group = this_form." + this_form[i].name);
						if (this_group.length > 0) {
							values = value.split(",");
							for (j=0; j<values.length; j++) {
								this_group[values[j]].checked = true;
							}
						} else {
							if (value === "0") {
								this_group.checked = true;
							}
						}
					} else if (this_form[i].type == "radio") {
						eval("this_group = this_form." + this_form[i].name);
						if (this_group.length > 0) {
							this_group[value].checked = true;
						} else {
							if (value === "0") {
								this_group.checked = true;
							}
						}
					}
					last_name = this_form[i].name;
				}
			}
		}
	}


// grab all name/value pairs from the URL and create JavaScript variables from them

	function get_vars() {
	
		if (location.href.indexOf('?') != -1) {
		
			query_string = location.href.substring(location.href.indexOf('?') + 1, location.href.length);
			query_pairs = query_string.split("&");
			
			for (n=0; n < query_pairs.length; n++) {
				pair = query_pairs[n].split("=");
				key = pair[0];
				value = pair[1];
				eval(key + " = '" + value + "'");
			}
			
			if (query_string.indexOf("args=") != -1) {
				args_pieces = args.split(",");
				for (n=0; n < args_pieces.length; n++) {
					nn = n + 1;
					eval("arg" + nn + " = '" + args_pieces[n] + "'");
				}
			}
		
		} else {
			query_string = ""; query_pairs = ""; template = ""; args = "";
		}
			
	}
	

// decide whether we've been flattened or not

	function is_flattened() {
		return ((location.href.indexOf("flattened") != -1)&&(location.href.indexOf(".html") != -1));
	}


// return a path to a page, given a page name, in either a flattened or unflattened format
// change script_extension depending on what version of Contemplate we're using

	function format_page_name(page_name) {
		script_extension = "php";
		if (is_flattened()) {
			page_name_formatted = "../flattened/" + page_name + ".html";
		} else {
			page_name_formatted = "../contemplate/assembler." + script_extension + "?page=" + page_name;
		}
		return page_name_formatted;
	}
			

// gets the name of the current page, even if we're rewriting URLs

	function get_page_name() {
		if (location.href.indexOf("page=") != -1) {
			page_name = location.href.substring(location.href.indexOf('page=') + 5, location.href.length);
		} else {
			page_name = location.href.substring(location.href.lastIndexOf('/') + 1, location.href.indexOf('.htm'));
		}
		return page_name;
	}
	
	
// trim whitespace from before and after a string

	function trim(string) {
		string = String(string);
		//string = string.replace(/^\s*(.*?)\s*$/, "$1"); // this single line should work instead of the following two lines, but the ? to make the middle part less greedy throws an error in Mac IE, where that modifier is apparently not supported
		string = string.replace(/^\s*/, "");
		string = string.replace(/\s*$/, "");
		return string;
	}
	
	
// return a value up to the delimiter, or the original value if the delimiter isn't there
// - this is used most often when parsing an argstring to set button states

	function crop(value, delimiter) {
		result = (value.indexOf(delimiter) != -1) ? value.substring(0, value.indexOf(delimiter)) : value ;
		return result;
	}


// open a popup window, specifying source, width, and height, and optionally a name; if no name, choose a random one

	function popup(source, width, height, window_name) {
		if (! window_name) { 
			now = new Date();
			window_name = now.getTime();
		} else {
			window_name = window_name.replace(/ /g, "_");
		}
		popup_window = window.open(source, window_name, "width=" + String(width) + ",height=" + String(height) + ",location=no,menubar=no,directories=no,toolbar=no,scrollbars=yes,resizable=yes,status=yes");
		popup_window.focus();
		// return popup_window; // oops, returning anything (even false) makes IE and Firefox load a page containing the return value
	}


// open a windowless popup with CSS and AJAX, specifying source, width and height, and top and left position
// this assumes we have div IDs called popup_background (for display and positioning) and popup_content (for sizing)
// width and height can be set to an integer or to "auto"

	function windowless_popup(source, width, height, top, left) {
		fill_windowless_popup("<div style=\"text-align: center;\"><img src=\"/thirdparty/magiczoom/ajax-loader.gif\"></div>"); // start with an empty box
		background_div = document.getElementById("popup_background");
		content_div = document.getElementById("popup_content");
		if (background_div.style.display == "block") {
			// if the popup is already showing, hide it if we click its link again
			close_windowless_popup();
		} else {
			content_div.style.width = (width == "auto") ? width : width + "px" ;
			content_div.style.height = (height == "auto") ? height : height + "px" ;
			background_div.style.top = top + "px";
			background_div.style.left = left + "px";
			background_div.style.display = "block";
		}
		delimiter = (source.indexOf("?") != -1) ? "&" : "?" ;
		source = source + delimiter + "template=blank.html"; // this removes the outer HTML from the content
		ajax_request("popup", source, "fill_windowless_popup(ajax_response)", "stop_windowless_popup()");
	}
	
	function fill_windowless_popup(HTML) {
		// put the retreived HTML into the popup
		document.getElementById("popup_content").innerHTML = HTML;
	}
	
	function stop_windowless_popup(HTML) {
		alert("Unable to load the popup content.");
	}
	
	function close_windowless_popup() {
		document.getElementById("popup_background").style.display = "none";
	}


// show an email link on a page in a way that spam harvesters can't see

	function show_email(user, domain, tld, label) {
		if (!label) { label = user + "@" + domain + "." + tld; }
		document.write("<a href='mailto:" + user + "@" + domain + "." + tld + "'>" + label + "<\/a>");
	}


// show either the Command or Control text depending on the platform

	function show_command_key() {
		key = (navigator.platform.indexOf("Win") != -1) ? "Control" : "Command" ;
		document.write(key);
	}


// write a random element from the specified array

	function write_random(array_name) {
		which = (Math.round(Math.random() * (arrayName.length - 1)));
		document.write(array_name[which]);
	}


// the simplest possible rollover function

	function swap(name, state) {
		eval("document.images." + name + ".src = " + name + "_" + String(state) + ".src");
	}
	

// a rollover function that supports multiple instances of each image on the same page

	function multiswap(image_name, instance_ID, state) {
		instance_name = (instance_ID) ? image_name + "_" + instance_ID : image_name ;
		eval("document.images." + instance_name + ".src = " + image_name + "_" + String(state) + ".src");
	}
	

// rollovers with sticky highlights
// - used in navigation frames where the buttons persist but other pages are changing

	var previous = null;
	var current = null;

	function sticky_swap(name, state, hold) {
		if (hold == 1) {
			// set previously lit button to normal
			previous = current;
			if (previous != null) {
				eval('document.images.' + previous + '.src = ' + previous + '_0.src');
			}
		}
		if ((name != null) && (name != current)) {
			// set new button state
			eval('document.images.' + name + '.src = ' + name + '_' + String(state) + '.src');
			if (hold == 1) { current = name; }
		}
	}
	

// rollovers with separate but linked button and label graphics
// - highlights both the button and label if you mouse over either one 
// - requires graphics to be named like "button_0.gif" and "button_label_0.gif"

	function label_swap(name, state) {
		eval('document.images.' + name + '.src = ' + name + '_' + String(state) + '.src');
		
		// change the label if one exists
		if (eval('document.images.' + name + '_label')) {
			eval('document.images.' + name + '_label.src = ' + name + '_label_' + String(state) + '.src');
		}
		
		// change the button if this is a label
		if (name.indexOf("_label") != -1) {
			name = name.substr(0, (name.length - 6));
			if (eval('document.images.' + name)) {
				eval('document.images.' + name + '.src = ' + name + '_' + String(state) + '.src');
			}
		}
	}
	

// write a block of code that preloads a list of graphics
// - this version makes on and off states and is most often used for button rollovers

// names - a comma-delimited list of button names (e.g. "home,about,contact")
// path - the path to the graphics; defaults to "../graphics" if not set (e.g. "../graphics/menus/home")
// extension - the file extension of the graphics files; defaults to "gif" if not set (e.g. "jpg")

	function preload_buttons(names, path, extension) {
		names = names.split(",");
		path = (path) ? path : "../graphics" ;
		extension = (extension) ? extension : "gif" ;
		for (var i=0; i<names.length; i++) {
			this_name = names[i];
			this_path = path + "/" + this_name;
			eval(this_name + "_0 = new Image()");
			eval(this_name + "_0.src = '" + this_path + "_0." + extension + "'");
			eval(this_name + "_1 = new Image()");
			eval(this_name + "_1.src = '" + this_path + "_1." + extension + "'");
		}
	}
	

// write a block of code that preloads a list of graphics
// - this version just makes an on state and is most often used for tips associated with button rollovers

// names - a comma-delimited list of button names (e.g. "home,about,contact")
// path - the path to the graphics; defaults to "../graphics" if not set (e.g. "../graphics/menus/home")
// extension - the file extension of the graphics files; defaults to "gif" if not set (e.g. "jpg")

	function preload_tips(names, path, extension) {
		names = names.split(",");
		path = (path) ? path : "../graphics" ;
		extension = (extension) ? extension : "gif" ;
		for (var i=0; i<names.length; i++) {
			this_name = names[i];
			this_path = path + "/" + this_name;
			eval(this_name + " = new Image()");
			eval(this_name + ".src = '" + this_path + "." + extension + "'");
		}
	}
	

// write a block of code that preloads a list of graphics
// - this version uses the same source file for each instance and is most often used for markers associated with button rollovers

// names - a comma-delimited list of instance names (e.g. "home_marker,about_marker,contact_marker")
// marker_name - the base name of the marker graphic
// path - the path to the graphics; defaults to "../graphics" if not set (e.g. "../graphics/menus/home")
// extension - the file extension of the graphics files; defaults to "gif" if not set (e.g. "jpg")

	function preload_markers(names, marker_name, path, extension) {
		names = names.split(",");
		path = (path) ? path : "../graphics" ;
		extension = (extension) ? extension : "gif" ;
		for (n=0; n < names.length; n++) {
			this_name = names[n];
			this_path = path + "/" + marker_name;
			eval(this_name + "_0 = new Image()");
			eval(this_name + "_0.src = '" + this_path + "_0." + extension + "'");
			eval(this_name + "_1 = new Image()");
			eval(this_name + "_1.src = '" + this_path + "_1." + extension + "'");
		}
	}
	

// leave this here to catch calls to the original preload script

	function make_preloads(names, path) {
		preload_buttons(names, path);
	}
	

// add the onmouseover and onmouseout events to images to enable their rollover behavior

// names - a comma-delimited list of button names (e.g. "home,about,contact")
// IDs - a comma-delimited list of record IDs used to distinguish different instances of the same button

	function add_rollover_events(names, IDs) {
		names = names.split(",");
		IDs = (IDs) ? IDs.split(",") : "" ;
		for (var j=0; j<IDs.length; j++) {
			for (var i=0; i<names.length; i++) {
				instance_name = (IDs) ? names[i] + "_" + IDs[j] : names[i] ;
				instance_ID = (IDs) ? IDs[j] : "" ;
				source_name = names[i];
				if (isset("document.images." + instance_name)) {
					eval("document.images." + instance_name + ".onmouseover = function() { multiswap('" + source_name + "', '" + instance_ID + "', 1); } ;");
					eval("document.images." + instance_name + ".onmouseout = function() { multiswap('" + source_name + "', '" + instance_ID + "', 0); } ;");
				}
			}
		}
	}


// search some menu code for a page name and set that page's link ID to "active"
// it would be better to scan the objects within the given div, look for the object whose href matches the page, and change that object's class, but this seems to work fine for now

	function set_active_button(menu_name, section, page) {
		if (page != "home") {
			menu_code = document.getElementById(menu_name).innerHTML; // note that IE capitalizes HTML tags, so we have to use case-insensitive regexps below
			
			// mark the current main menu item based on the section
			if (menu_name == "menu_main") {
				re = new RegExp("(\"/" + section + "/\\w+\.html\")", "g");
				menu_code = menu_code.replace(re, "$1 id=\"active\"", "i");
			}
			
			// mark the current section menu item based on the page
			if (menu_name == "menu_section") {
				re = new RegExp("(\\W" + page + "\.html\")", "g");
				menu_code = menu_code.replace(re, "$1 id=\"active\"", "i");
			}
			
			document.getElementById(menu_name).innerHTML = menu_code;
		}
	}
	

// ajax functions
// 2009-01-30 - added support for synchronous connections (by setting asynchronous to false)
// 2009-04-16 - tweaked handling of synchronous connections for Firefox
// 2009-08-23 - added an ID to each request so multiple requests don't interfere with each other, and got rid of the "passthrough" variables for the same reason
// 2009-11-27 - added the Ajax-Request header to all requests in case we need to handle those differently somewhere

	function ajax_initialize(ID) {
		if (!isset("ajax", "object")) {
			ajax = new Object;
		}
		ajax[ID] = "";
		if (typeof(XMLHttpRequest) != "undefined") {
			ajax[ID] = new XMLHttpRequest();
		} else {
			// need to check for additional versions here?
			ajax[ID] = new ActiveXObject("Microsoft.XMLHTTP");
		}
		//return ajax[ID];
	}
	
	function ajax_request(ID, URL, success_code, failure_code, method, mode, data, wait) {
		// method: GET or POST
		// mode: text or XML
		// data: query string
		// wait: 1=asynchronous, 0=synchronous
	
		// in IE, we have to initialize before every request or subsequent request don't call the monitor
		ajax_initialize(ID);
		
		// set some defaults
		if (!failure_code) { failure_code = "alert('There was a problem processing your request.');"; }
		if (!method) { method = "GET"; }
		if (!mode) { mode = "text"; }
		asynchronous = (wait) ? false : true ;
		
		// if we're using the GET method but passing in data, add it to the URL
		if ((method == "GET")&&(data)) {
			delimiter = (URL.indexOf("?") != -1) ? "&" : "?" ;
			URL += delimiter + data;
		}
		
		// perform the request
		if (asynchronous) { // listen for a response action
			ajax[ID].onreadystatechange = function() { ajax_monitor(ID, mode, success_code, failure_code); } ;
		}
		ajax[ID].open(method, URL, asynchronous);
		ajax[ID].setRequestHeader("Ajax-Request", "1");
		if (method == "POST") {
			ajax[ID].setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        	ajax[ID].send(data);
		} else {
        	ajax[ID].send(null);
        }
		if (!asynchronous) { // just run the response action now; Firefox doesn't change the ready state
			ajax_monitor(ID);
		}
	}
	
	function ajax_monitor(ID, mode, success_code, failure_code) {
		if (ajax[ID].readyState == 4) {
			if (ajax[ID].getResponseHeader("Ajax-Redirect")) {
				top.location = "index.php?page=" + ajax[ID].getResponseHeader("Ajax-Redirect");
			} else {
				if (ajax[ID].status == 200) {
					if (mode == "XML") {
						// don't forget to set a Content-Type: text/xml header in the script that generates this!
						ajax_response = ajax[ID].responseXML;
					} else {
						ajax_response = ajax[ID].responseText;
					}
					eval(success_code);
				} else {
					ajax_response = ajax[ID].statusText;
					eval(failure_code);
				}
			}
		}
	}
	
	function ajax_serialize(input, encode) {
		// convert an object into a string of name/value pairs for including in an HTTP request
		// we need to encode POST values, but not GET values
		output = new Array();
		for (key in input) {
			value = input[key];
			if (encode) {
				value = encodeURIComponent(value);
			}
			output[output.length] = key + "=" + value;
		}
		output = output.join("&");
		return output;
	}
	
	function ajax_unserialize(input, decode) {
		// convert a string of name/value pairs into an object for retrieving from an HTTP response
		input = input.split("&");
		output = new Object();
		for (var i=0; i<input.length; i++) {
			pair = input[i].split("=");
			key = pair[0];
			value = pair[1];
			if (decode) {
				value = decodeURIComponent(value);
			}
			output[key] = value;
		}
		return output;
	}
	
	function xml_to_object(xml) {
		// convert an XML document (an XML ajax_response) to an object for easy data access
		// this doesn't iterate currently, so it only goes one level deep
		output = new Object();
		items = ajax_response.getElementsByTagName("item");
		for (var i=0; i<items.length; i++) {
			output[i] = new Object();
			nodes = items[i].childNodes;
			for (j=0; j<nodes.length; j++) {
				if (nodes[j].nodeType == 1) {
					name = nodes[j].nodeName;
					value = nodes[j].childNodes[0].nodeValue;
					output[i][name] = value;
				}
			}
		}
		return output;
	}
	

// use AJAX to reload a menu we created with our PHP make_menu function

	function reload_menu(form, table, name, value, text, filter, sort, multiple, selected, javascript_value) {

		// the arguments here work the same as the PHP function
		// javascript_value is the result of a JavaScript expression we can optionally put in the last argument; that will run when the function is called and pass the current value in; this allows us to set up the function call in PHP before JavaScript executes
		// table needs to be listed in the allowed_tables array of reload.php

		filter = filter.replace(/javascript_value/, javascript_value);
		
		request_URL = "/shared/reload.php?type=menu";
		request_URL += "&table=" + table;
		request_URL += "&name=" + name;
		request_URL += "&value=" + value;
		request_URL += "&text=" + text;
		request_URL += "&filter=" + escape(filter);
		request_URL += "&sort=" + sort;
		request_URL += "&multiple=" + multiple;
		
		// show a "loading" message in the current menu
		eval("menu_name = document." + form + "." + name + ";");
		menu_name.options[0].text = menu_name.options[0].text + " (loading)";
		menu_name.selectedIndex = 0;

		ajax_request(name, request_URL, "reload_menu_finish('" + form + "', '" + name + "', ajax_response, '" + selected + "')", "", "", "HTML");
		
	}
	
	function reload_menu_finish(form, name, menu_data, selected) {
	
		// get the name and size of the current menu
		eval("menu_name = document." + form + "." + name + ";");
		menu_count = menu_name.options.length;

		// get ready to process the new menu data
		// why are we doing this instead of just passing the entire menu back? (that seems to work for the checkboxes, below)
		// because this doesn't require an enclosing div as the checkboxes do
		menu_data = (menu_data) ? menu_data.split("&") : new Array() ;
		menu_data_count = menu_data.length;
		
		// first, empty out the menu
		for (var i=menu_count; i>1; i--) {
			menu_name.options[i-1] = null;
		}

		// then load the new options
		for (var i=0; i<menu_data_count; i++) {
			this_menu_item = menu_data[i].split("=");
			this_value = this_menu_item[0];
			this_text = this_menu_item[1];

			menu_name.options[i+1] = new Option(this_text, this_value);
			
			if (this_value == selected) {
				menu_name.options[i+1].selected = true;
			}
		}

		// remove the "loading" message from the menu
		menu_name.options[0].text = menu_name.options[0].text.replace(/ \(loading\)/g, "");

	}


// use AJAX to reload a set of checkboxes we created with our PHP make_checkboxes function

	function reload_checkboxes(form, table, name, value, text, filter, sort, delimiter, selected, javascript_value) {
		
		// the arguments here work the same as the PHP function, except for limit, which takes an evaluatable snippet of PHP code rather than a list
		// javascript_value is the result of a JavaScript expression we can optionally put in the last argument; that will run when the function is called and pass the current value in
		// name is the name of a div tag that must surround the checkbox area
		// table needs to be listed in the allowed_tables array of reload.php

		filter = filter.replace(/javascript_value/, javascript_value);
		
		request_URL = "/shared/reload.php?type=checkboxes";
		request_URL += "&table=" + table;
		request_URL += "&name=" + name;
		request_URL += "&value=" + value;
		request_URL += "&text=" + text;
		request_URL += "&filter=" + escape(filter);
		request_URL += "&sort=" + sort;
		request_URL += "&delimiter=" + delimiter;
		request_URL += "&selected=" + selected;
		
		ajax_request(name, request_URL, "reload_checkboxes_finish('" + form + "', '" + name + "', ajax_response)", "", "HTML");
		
	}
	
	function reload_checkboxes_finish(form, name, checkboxes_data) {
		
		// get the div surrounding the current checkboxes
		checkboxes_div = document.getElementById(name);

		// then load the new checkboxes
		checkboxes_div.innerHTML = checkboxes_data;

	}


// provide some specialized functions for handling the images for content ratings	

	function rollover_ratings(ID, rating, full_icon, none_icon, label) {
		if (label) {
			rating_label_original = document.getElementById("rating_" + ID + "_label").innerHTML;
			document.getElementById("rating_" + ID + "_label").innerHTML = label;
		}
		for (var i=1; i<=5; i++) {
			if (i <= rating) {
				this_icon = full_icon;
			} else {
				this_icon = none_icon;
			}
			eval("rating_" + i + "_original = document.rating_" + ID + "_" + i + ".src;");
			eval("document.rating_" + ID + "_" + i + ".src = '" + this_icon + "';");
		}
	}
	
	function restore_ratings(ID, label) {
		for (var i=1; i<=5; i++) {
			eval("document.rating_" + ID + "_" + i + ".src = rating_" + i + "_original;");
		}
		if (label) {
			document.getElementById("rating_" + ID + "_label").innerHTML = rating_label_original;
		}
	}
	
	function set_rating(ID, rating, full_icon, none_icon, label) {
		for (var i=1; i<=5; i++) {
			if (i <= rating) {
				this_icon = full_icon;
			} else {
				this_icon = none_icon;
			}
			eval("document.rating_" + ID + "_" + i + ".src = '" + this_icon + "';");
			eval("rating_" + i + "_original = document.rating_" + ID + "_" + i + ".src;");
		}
		name = (ID) ? "rating_" + ID : "rating" ;
		document.getElementById(name + "_value").value = rating;
		if (label) {
			document.getElementById("rating_" + ID + "_label").innerHTML = rating_label_original;
		}
	}
	
	function submit_rating(table, ID, rating, labels) {
		document.getElementById("rating_" + ID).innerHTML = "Submitting...";
		URL = "/shared/rating_submit.php?table=" + table + "&ID=" + ID + "&rating=" + rating;
		success_code = "reload_rating('" + table + "', '" + ID + "', 'submit', " + labels + ")"; // change mode to display to "lock" the rating after submitting it
		failure_code = "reload_rating('" + table + "', '" + ID + "', 'submit', " + labels + ")";
		ajax_request("rating_"+ID, URL, success_code, failure_code);
	}
	
	function reload_rating(table, ID, mode, labels) {
		URL = "/shared/rating_reload.php?table=" + table + "&ID=" + ID + "&mode=" + mode + "&labels=" + labels;
		success_code = "document.getElementById('rating_" + ID + "').innerHTML = ajax_response;";
		failure_code = "";
		ajax_request("rating_"+ID, URL, success_code, failure_code);
	}


// figure out whether a variable has been set or not without generating an undefined error if it hasn't
// possible types are "undefined", "object", "boolean", "number", "string" or "function", with everything else being "object"
// this does not work on function arguments inside a function; even if the argument is set, this still returns false
// but in that case we can just check for a value without getting an error

	function isset(variable, type) {
		if (type) {
			eval("result = (typeof(" + variable + ") == type)");
		} else {
			eval("result = (typeof(" + variable + ") != 'undefined')");
		}
		return result;
	}


// capitalize the first letter of a string, and make the others lowercase

	function ucfirst(str) {
		str = str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
		return str;
	}
	

// capitalize the first letter of every word in a string, and make the others lowercase
// this also collapses multiple spaces into single spaces, for better or for worse

	function ucwords(str) {
		result = new Array();
		str = str.split(/\s/);
		count = str.length;
		for (var i=0; i<count; i++) {
			result[i] = ucfirst(str[i]);
		}
		result = result.join(" ");
		return result;
	}
	

// add a value to an array, creating the array first if it does not already exist

	function add_to_array(array_name, value) {
		eval("array_exists = isset('" + array_name + "', 'object');");
		if (!array_exists) {
			eval(array_name + " = new Array();");
		}
		eval("value_index = get_position(value, " + array_name + ");");
		if (value_index == -1) {
			eval(array_name + ".push(value);");
			return 1;
		} else {
			// if the value was already in the array, return false
			return 0;
		}
	}


// remove an element from an array

	function remove_from_array(array_name, value) {
		eval("value_index = get_position(value, " + array_name + ");");
		if (value_index > -1) {
			eval(array_name + ".splice(value_index, 1);");
			return 1;
		} else {
			// if the value wasn't in the array, return false
			return 0;
		}
	}


// returns the index of an array element, something that ought to be built into JavaScript but isn't!
// returns -1 if not present

	function get_position(string, array) {
		var count = array.length;
		for (var i=0; i<count; i++) {
			if (array[i] == string) {
				return i;
				break;
			}
		}
		return -1;
	}


// select the given values or text in a menu
// set values to "all" to select all the values

	function set_menu(form_name, field_name, text_or_value, values) {
		eval("these_options = document." + form_name + ".elements['" + field_name + "'].options");
		all = (values == "all");
		values = String(values).split(",");
		for (var i=0; i<these_options.length; i++) {
			eval("this_value = these_options[i]." + text_or_value);
			if ((all)||(get_position(this_value, values) != -1)) {
				these_options[i].selected = true;
			} else {
				these_options[i].selected = false;
			}
		}
		return "";
	}


// check or uncheck the given values in a checkbox set
// set values to "all" to set all the checkboxes

	function set_checkbox(form_name, field_name, values, checked) {
		eval("these_elements = document." + form_name + ".elements['" + field_name + "']");
		all = (values == "all");
		values = String(values).split(",");
		if (these_elements.length > 0) {
			for (var i=0; i<these_elements.length; i++) {
				eval("this_value = these_elements[i].value");
				if ((all)||(get_position(this_value, values) != -1)) {
					these_elements[i].checked = checked;
				} else {
					these_elements[i].checked = (!checked);
				}
			}
		} else {
			eval("this_value = these_elements.value");
			if (get_position(this_value, values) != -1) {
				these_elements.checked = checked;
			}
		}
		return "";
	}


// check the given value in a radio button set

	function set_radio(form_name, field_name, value) {
		eval("these_elements = document." + form_name + ".elements['" + field_name + "']");
		for (var i=0; i<these_elements.length; i++) {
			eval("this_value = these_elements[i].value");
			if (this_value == value) {
				these_elements[i].checked = true;
			} else {
				these_elements[i].checked = false;
			}
		}
		return "";
	}


// set date menus to a new SQL-standard date
// leave date blank to select today's date
// set to -1 to clear the menu

	function select_date(form_and_menu, new_date) {
		if (new_date != "-1") {
			if (new_date == "") {
				date = new Date();
				
				day = date.getDate();
				month = date.getMonth() + 1;
				year = date.getFullYear();
				
			} else {
				date = new_date;
				
				year = date.substring(0, date.indexOf("-"));
				month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
				day = date.substring(date.lastIndexOf("-") + 1);
				
			}
					
			eval("document." + form_and_menu + "_month.selectedIndex = month");
			eval("document." + form_and_menu + "_day.selectedIndex = day");
			eval("document." + form_and_menu + "_year.selectedIndex = year - document." + form_and_menu + "_year.options[1].value + 1");
		} else {
			eval("document." + form_and_menu + "_month.selectedIndex = 0");
			eval("document." + form_and_menu + "_day.selectedIndex = 0");
			eval("document." + form_and_menu + "_year.selectedIndex = 0");
		}
	}


// set time menus to a new SQL-standard time
// leave time blank to select the current time
// set to -1 to clear the menu

	function select_time(form_and_menu, new_time, increment) {
		if (new_time != "-1") {
			if (new_time == "") {
				time = new Date();
				
				hours = time.getHours();
				minutes = time.getMinutes();
				seconds = time.getSeconds();

			} else {
				time = new_time;
				
				hours = time.substring(0, time.indexOf(":"));
				minutes = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
				seconds = time.substring(time.lastIndexOf(":") + 1);
				
			}
			
			if (hours >= 12) {
				ampm_index = 2;
				if (hours > 12) {
					hours = hours - 12;
				}
			} else {
				ampm_index = 1;
				if (hours == 0) {
					hours = 12;
				}
			}
			
			if (increment) {
				// this assumes that the increment is for minutes only and not for seconds
				minutes = Math.floor(minutes / increment);
			}
			
			eval("document." + form_and_menu + "_hours.selectedIndex = hours");
			eval("document." + form_and_menu + "_minutes.selectedIndex = minutes + 1");
			eval("document." + form_and_menu + "_seconds.selectedIndex = seconds + 1");
			eval("document." + form_and_menu + "_ampm.selectedIndex = " + ampm_index);
		} else {
			eval("document." + form_and_menu + "_hours.selectedIndex = 0");
			eval("document." + form_and_menu + "_minutes.selectedIndex = 0");
			eval("document." + form_and_menu + "_seconds.selectedIndex = 0");
			eval("document." + form_and_menu + "_ampm.selectedIndex = 0");
		}
	}


// get the individual values selected for a set of date menus and join them into an SQL date string
// if any of the individual menus are unset, we'll return an empty value

	function get_date(form_name, field_name) {
		eval("year = document." + form_name + "." + field_name + "_year.options[document." + form_name + "." + field_name + "_year.selectedIndex].value;");
		eval("month = document." + form_name + "." + field_name + "_month.options[document." + form_name + "." + field_name + "_month.selectedIndex].value;");
		eval("day = document." + form_name + "." + field_name + "_day.options[document." + form_name + "." + field_name + "_day.selectedIndex].value;");

		if ((year != "0")&&(month != "0")&&(day != "0")) {
			date = year + "-" + month + "-" + day;
		} else {
			date = ""; // our classes let us use "" for convenience and convert it to 0000-00-00 when saving to the database
		}
		
		return date;
	}


// get the individual values selected for a set of time menus and join them into an SQL time string
// if any of the individual menus are unset, we'll return an empty value

	function get_time(form_name, field_name) {
		eval("hours = document." + form_name + "." + field_name + "_hours.options[document." + form_name + "." + field_name + "_hours.selectedIndex].value;");
		eval("minutes = document." + form_name + "." + field_name + "_minutes.options[document." + form_name + "." + field_name + "_minutes.selectedIndex].value;");
		eval("seconds = document." + form_name + "." + field_name + "_seconds.options[document." + form_name + "." + field_name + "_seconds.selectedIndex].value;");
		eval("ampm = document." + form_name + "." + field_name + "_ampm.options[document." + form_name + "." + field_name + "_ampm.selectedIndex].value;");
	
		if ((hours != "0")&&(minutes != "0")&&(seconds != "0")&&(ampm != "0")) {
			if ((ampm == "pm")&&(hours < 12)) { hours = hours + 12; }
			if ((ampm == "am")&&(hours == 12)) { hours = 0; }
			time = hours + ":" + minutes + ":" + seconds;
		} else {
			date = ""; // our classes let us use "" for convenience and convert it to 00:00:00 when saving to the database
		}
		
		return time;
	}
	
	
// loop through a set of checkboxes and build a comma-delimited list of selected values

	function get_checkbox(form_name, field_name) {
		eval("these_elements = document." + form_name + ".elements['" + field_name + "']");
		values = new Array();
		if (these_elements.length > 0) {
			for (var i=0; i<these_elements.length; i++) {
				if (these_elements[i].checked) {
					values[values.length] = these_elements[i].value;
				}
			}
		} else {
			if (these_elements.checked) {
				values[values.length] = these_elements.value;
			}
		}
		values = values.join(",");
		return values;
	}


// loop through a set of radio buttons and find the selected value

	function get_radio(form_name, field_name) {
		eval("these_elements = document." + form_name + ".elements['" + field_name + "']");
		value = "";
		if (these_elements.length > 0) {
			for (var i=0; i<these_elements.length; i++) {
				if (these_elements[i].checked) {
					value = these_elements[i].value;
					break;
				}
			}
		} else {
			if (these_elements.checked) {
				value = these_elements.value;
			}
		}
		return value;
	}


// get the selected text or value for a menu
// selectedIndex doesn't access all selected options in a multiple-select menu, so those aren't supported yet

	function get_menu(form_name, field_name, text_or_value) {
		option_selected = document.forms[form_name].elements[field_name].options[document.forms[form_name].elements[field_name].selectedIndex];
		value = (text_or_value == "text") ? option_selected.text : option_selected.value ;
		return value;
	}


// set a base text size for each platform; can be used in conjunction with static font tags for face and color
// remember to close this tag with a static </font> later in your document	

	function text_size(mac, win) {
		if (navigator.appVersion.indexOf("Mac") != -1) {
			document.write("<font face=geneva,arial size=" + mac + ">");
		} else {
			document.write("<font face=geneva,arial size=" + win + ">");
		}
	}


// add an onchange event to every element in a form

	function set_form_onchange(form_name, function_name, skip_fields) {
		eval("these_elements = document." + form_name + ".elements");
		for (var i=0; i<these_elements.length; i++) {
			skip_fields_match = new RegExp("\\b" + these_elements[i].name + "\\b");
			if (!skip_fields.match(skip_fields_match)) {
				eval("these_elements[i].onchange = " + function_name + ";");
			}
		}
	}


// jumble up some text for safer transfer in places where cookies or PHP encryption can't go

	// direction: 1 encrypts, 0 decrypts

	// 2004-08-27 - we're no longer removing and restoring spaces; that should be the job of the code before and after calling this function, since it is not always desirable

	function pseudo_crypt(input, direction) { 
		output = "";
		palette = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+&=:/"; // any other characters will be untouched
		key = "everygoodboydeservesfudge"; // can only contain members of palette
		key_position = 0;
		for (var i=0; i<input.length; i++) {
			input_char = palette.indexOf(input.charAt(i));
			if (input_char != -1) {
				key_char = key.charAt(key_position);
				offset = palette.indexOf(key_char);
				if (direction == 1) {
					offset = ((input_char + offset) > (palette.length - 1)) ? offset - palette.length : offset ;
					output += palette.charAt(input_char + offset);
				} else {
					offset = ((input_char - offset) < 0) ? offset - palette.length : offset ;
					output += palette.charAt(input_char - offset);
				}
				key_position++;
				key_position = (key_position > key.length) ? 0 : key_position ;
			} else {
				output += input.charAt(i);
			}
		}
		return output;
	}
	

// take the current URL and add a name/value pair to it, without duplication if it already exists
// passing an empty value removes the name/value pair

	// 2010-05-14 - added support for multiple values ending in []; when the name ends in [], the value is split by a comma
	// 2010-06-08 - fixed a bug if the initial URL doesn't have a query string

	function update_query_string(name, value) {
		if (document.URL.indexOf("?") != -1) {
			URL_parts = document.URL.split("?");
			URL_base = URL_parts[0];
			URL_query = URL_parts[1];
		} else {
			URL_base = document.URL;
			URL_query = "";
		}
		
		name = name.replace(/\[\]/, "%5B%5D");
		regexp = new RegExp(name + "=[^&]*&?", "g");
		URL_query = URL_query.replace(regexp, "");
		URL_query = URL_query.replace(/&$/, "");

		if (value !== "") {
			delimiter = (URL_query) ? "&" : "" ;
			if (name.match(/%5B%5D/)) {
				value = value.split(",");
				for (var i=0; i<value.length; i++) {
					URL_query += delimiter + name + "=" + value[i];
					delimiter = "&";
				}
			} else {
				URL_query += delimiter + name + "=" + value;
			}
		}

		if (URL_query) {
			URL = URL_base + "?" + URL_query;
		} else {
			URL = URL_base;
		}
		return URL;
	}


// returns true if the mouseout event comes from the same element it was attached to
// put this in the HTML onmouseout tag before the code you want to run: onmouseout="if (stop_child_events(event, this)) run_this_code()"
// courtesy of http://www.dynamic-tools.net/toolbox/isMouseLeaveOrEnter/

	function stop_child_events(event, handler) {
		if (event.type != 'mouseout' && event.type != 'mouseover') return false;
		var reltg = event.relatedTarget ? event.relatedTarget :
		event.type == 'mouseout' ? event.toElement : event.fromElement;
		while (reltg && reltg != handler) reltg = reltg.parentNode;
		return (reltg != handler);
	}


// get a CSS rule object from its selector text
// we can use this to change a class's attributes: get_CSS_rule("{selector}").style.{attribute} = "{value}";

	function get_CSS_rule(selector) {
		for (s=0; s<document.styleSheets.length; s++) {
			if (document.styleSheets[s].rules) {
				selector = selector.toLowerCase(); // we'll do case-insensitive comparisons here because IE makes all the HTML tags upper-case
				for (r=0; r<document.styleSheets[s].rules.length; r++) {
					if (document.styleSheets[s].rules[r].selectorText.toLowerCase() == selector) {
						return document.styleSheets[s].rules[r];
					}
				}
			} else if (document.styleSheets[s].cssRules) {
				for (r=0; r<document.styleSheets[s].cssRules.length; r++) {
					if (document.styleSheets[s].cssRules[r].selectorText == selector) {
						return document.styleSheets[s].cssRules[r];
					}
				}
			}
		}
		return null;
	}


// we can use this to submit a form from JavaScript without skipping the onsubmit code
// this also allows passing some additional arguments along with the form
// and sometimes we -want- to skip the onsubmit code, like when we back up in a multi-page form
// prevent_repeats disables the button after it is clicked the first time

	function submit_form(form_name, extra_args, skip_onsubmit, prevent_repeats) {
		stop_submit = false;
		if (prevent_repeats) {
			if (!isset("submit_form_prevent_repeats")) {
				submit_form_prevent_repeats = true;
			} else {
				stop_submit = true;
			}
		}
		if (!stop_submit) {
			this_form = document.forms[form_name];
			if (this_form.target == "opener") {
				window.opener.name = "opener";
			}
			if ((skip_onsubmit)||(!this_form.onsubmit)||(this_form.onsubmit())) {
				if (extra_args) {
					delimiter = (this_form.action.indexOf("?") != -1) ? "&" : "?" ;
					this_form.action = this_form.action + delimiter + extra_args;
				}
				this_form.submit();
			}
		}
	}
	

// this is handy for preventing the Return key from submitting forms (set form action to "JavaScript:nothing()")
	
	function nothing() {}
		

// open popup windows with globally defined sizes

	function open_popup(URL) {
		var name = URL.replace(/.*\/\w+\/(\w+)\.\w+.*/, "$1");
		if ((isset("popup_window", "object"))&&(popup_window.name == name)) {
			popup_window.focus();
		} else {
			switch (name) {
				case "images_story": popup(URL, 450, 525, name); break;
				case "terms": popup(URL, 500, 500, name); break;
				case "search_tips": popup(URL, 525, 200, name); break;
				case "help_user_admin": popup(URL, 425, 200, name); break;
				case "help_user_delete": popup(URL, 425, 200, name); break;
			}
		}
	}


// this is pretty basic but we might as well make a function to match the PHP function

	function close_popup(refresh_opener) {
		if (refresh_opener) {
			window.opener.location = window.opener.location;
		}
		close();
	}
	

// load a new page into the opener window

	function change_opener(URL, close_popup) {
		window.opener.location = URL;
		if (close_popup) {
			close();
		}
	}
	

// this is for if we don't know if we're in the main window or a popup window

	function change_main_or_opener(URL, close_popup) {
		if (window.opener) {
			change_opener(URL, close_popup);
		} else {
			location = URL;
		}
	}
	

// show an active version of the button and hide an inactive version, or vice-versa

	function toggle_button_state(name, state) {
		state = parseInt(state);
		if (state == 1) {
			if (document.getElementById(name + "_active")) { document.getElementById(name + "_active").style.display = ""; }
			if (document.getElementById(name + "_inactive")) { document.getElementById(name + "_inactive").style.display = "none"; }
			if (document.getElementById(name + "_disabled")) { document.getElementById(name + "_disabled").style.display = "none"; }
		} else if (state == 0) {
			if (document.getElementById(name + "_active")) { document.getElementById(name + "_active").style.display = "none"; }
			if (document.getElementById(name + "_inactive")) { document.getElementById(name + "_inactive").style.display = ""; }
			if (document.getElementById(name + "_disabled")) { document.getElementById(name + "_disabled").style.display = "none"; }
		} else if (state == -1) {
			if (document.getElementById(name + "_active")) { document.getElementById(name + "_active").style.display = "none"; }
			if (document.getElementById(name + "_inactive")) { document.getElementById(name + "_inactive").style.display = "none"; }
			if (document.getElementById(name + "_disabled")) { document.getElementById(name + "_disabled").style.display = ""; }
		}
	}


// when clicking a main menu item, set its button and submenu styles

	function toggle_submenu(menu, submenu) {
		if (document.getElementById("submenu_" + submenu).className.indexOf("active") == -1) {
			document.getElementById("submenu_" + submenu).className = menu + "_submenu active";
		} else {
			document.getElementById("submenu_" + submenu).className = menu + "_submenu";
		}
	}
	

// pass arguments into a frameset document to automatically set any of the frame locations
// place  onload=setFrames()  into the frameset tag, then include  framename=location.html  in your URL

	function set_frames() {

		if (location.href.indexOf('?') != -1) {

			query_string = location.href.substring(location.href.indexOf('?') + 1, location.href.length);
			query_pairs = query_string.split("&");
			
			for (n=0; n < query_pairs.length; n++) {
				
				pair = query_pairs[n].split("=");
				key = pair[0];
				value = pair[1];
				
				for (nn=0; nn < frames.length; nn++) {
					if (key == frames[nn].name) {
						eval(key + ".location = '" + value + "'");
						break;
					}
				}
				
			}

		}

	}


// if we nest a larger container inside a smaller container, this lets us move it around (currently just up and down)
// this requires that the inner and outer containers use position: relative, and the outer uses overflow: hidden

	function scroll_inner_content(outer_ID, inner_ID, direction) {
		overlap = 10; // scroll the height of the container box minus this amount
		
		inner_height = document.getElementById(inner_ID).clientHeight;
		outer_height = document.getElementById(outer_ID).clientHeight;
		if (outer_height > inner_height) { return; }

		max = 0;
		min = 0 - (inner_height - outer_height);
		increment = outer_height - overlap;
		
		current = document.getElementById(inner_ID).offsetTop;
		neww = (direction == "up") ? current + increment : current - increment ;
		if (neww > max) { neww = max; }
		if (neww < min) { neww = min; }
		
		document.getElementById(inner_ID).style.top = neww + "px";
	}
	

// control regular list pages

	function pagination(start) {
		location = update_query_string("start", start);
	}
	
	// this one isn't currently used
	function sort(sort) {
		location = update_query_string("sort", sort);
	}
	

// control the AJAX-driven list pages

	function list_sort(column) {
		document.filters.sort.value = column;
		filters_view();
	}

	function list_page(start) {
		document.filters.start.value = start;
		filters_view();
	}

	function list_load(ajax_URL, filter_data) {
		if (isset("loaded")) { list_clear_feedback(); } // this avoids clearing feedback the first time the page loads
		filter_data = ajax_serialize(filter_data, 0);
		success_code = "document.getElementById('list').innerHTML = ajax_response;";
		failure_code = "alert(\"There was an error loading the list.\");";
		ajax_request("list", ajax_URL, success_code, failure_code, "POST", "text", filter_data);
		loaded = 1;
	}
	
	function list_show_feedback(message) {
		document.getElementById("feedback").innerHTML = message;
	}
	
	function list_clear_feedback() {
		document.getElementById("feedback").innerHTML = "";
	}
	

// for debugging
// from http://weblogs.asp.net/skillet/archive/2006/03/23/440940.aspx

	function show_object(obj, name, indent, depth) {
		if (!name) { name = "items"; }
		if (!indent) { indent = ""; }
		if (!depth) { depth = 0; }
		if (depth > 10) {
			return indent + name + ": <Maximum Depth Reached>\n";
		}
		if (typeof obj == "object") {
			var child = null;
			var output = indent + name + "\n";
			indent += "\t";
			for (var item in obj) {
				try {
					child = obj[item];
				} catch (e) {
					child = "<Unable to Evaluate>";
				}
				if (typeof child == "object") {
					output += show_object(child, item, indent, depth + 1);
				} else {
					output += indent + item + ": " + child + "\n";
				}
			}
			return output;
		} else {
			return obj;
		}
	}



