// 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);
		}
	}


// 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;
	}


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

	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(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;
				}
			}
		}
	}


// 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);
	}


// the simplest possible rollover function

	function swap(name, state) {
		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 + "'");
		}
	}
	

// 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

	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);
		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_size_menu(form, color_menu_name, size_menu_name, quantity_menu_name, item) {
	
		// reload the size while checking what sizes are available for this item in the selected color
		
		color = get_menu(form, color_menu_name, "value");
		size = get_menu(form, size_menu_name, "value");
		
		// don't proceed if a color isn't selected
		if (!parseInt(color)) { return; }

		request_URL = "/shared/reload.php?type=size_menu";
		request_URL += "&item=" + item;
		request_URL += "&color=" + color;
		request_URL += "&size=" + size;
		
		// show a "loading" message in the current menu
		eval("menu_name = document." + form + "." + size_menu_name + ";");
		menu_name.options[0].text = menu_name.options[0].text + " (loading)";
		menu_name.selectedIndex = 0;

		// since the programmatic reload of the size menu doesn't trigger its onchange handler, we need to also reload the quantity menu when this reload is done
		next = "reload_quantity_menu('" + form + "', '" + color_menu_name + "', '" + size_menu_name + "', '" + quantity_menu_name + "', '" + item + "')";

		ajax_request(size_menu_name, request_URL, "reload_menu_finish('" + form + "', '" + size_menu_name + "', ajax_response, " + size + "); " + next, "", "", "HTML");
		
	}
	
	function reload_quantity_menu(form, color_menu_name, size_menu_name, quantity_menu_name, item) {
	
		// reload the size while checking what sizes are available for this item in the selected color and size
		
		color = get_menu(form, color_menu_name, "value");
		size = get_menu(form, size_menu_name, "value");
		quantity = get_menu(form, quantity_menu_name, "value");
		
		// don't proceed if a color or size isn't selected
		if (!parseInt(color)) { return; }
		if (!parseInt(size)) { return; }

		request_URL = "/shared/reload.php?type=quantity_menu";
		request_URL += "&item=" + item;
		request_URL += "&color=" + color;
		request_URL += "&size=" + size;
		request_URL += "&quantity=" + quantity;
		
		// show a "loading" message in the current menu
		eval("menu_name = document." + form + "." + quantity_menu_name + ";");
		menu_name.options[0].text = menu_name.options[0].text + " (loading)";
		menu_name.selectedIndex = 0;

		ajax_request(quantity_menu_name, request_URL, "reload_menu_finish('" + form + "', '" + quantity_menu_name + "', ajax_response, " + quantity + ")", "", "", "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 = "Your rating";
		}
		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) {
		document.getElementById("rating_" + ID).innerHTML = "Submitting...";
		URL = "/shared/rating_submit.php?table=" + table + "&ID=" + ID + "&rating=" + rating;
		success_code = "reload_rating('" + table + "', '" + ID + "', 'display')";
		failure_code = "reload_rating('" + table + "', '" + ID + "', 'submit')";
		ajax_request("rating_"+ID, URL, success_code, failure_code);
	}
	
	function reload_rating(table, ID, mode) {
		URL = "/shared/rating_reload.php?table=" + table + "&ID=" + ID + "&mode=" + mode;
		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
// actually, we can't select multiple options in a multiple-select menu via selectedIndex, so this doesn't quite work in that case

	function set_menu(form_name, field_name, text_or_value, values) {
		eval("these_options = document." + form_name + ".elements['" + field_name + "'].options");
		values = String(values).split(",");
		for (var i=0; i<these_options.length; i++) {
			eval("this_value = these_options[i]." + text_or_value);
			if (get_position(this_value, values) != -1) {
				eval("document." + form_name + "." + field_name + ".selectedIndex = " + i);
			}
		}
		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;
	}
	

// 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

	// 08-27-04 - 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
	
	function update_query_string(name, value) {
		URL = document.URL;
		regexp = new RegExp("[&\?]?" + name + "=[^&]*");
		URL = URL.replace(regexp, "");
		if (value !== "") {
			delimiter = (URL.indexOf("?") != -1) ? "&" : "?" ;
			URL = URL + delimiter + name + "=" + value;
		}
		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 "swatches_info": windowless_popup(URL, 336, "auto", 370, 560); break;
				case "samples_info": windowless_popup(URL, 336, "auto", 370, 560); break;
				case "wedding_party_info": windowless_popup(URL, 336, "auto", 370, 560); break;
				case "size_chart_popup": windowless_popup(URL, 336, "auto", 370, 560); break;
				case "wedding_search": popup(URL, 400, 300, name); break;
				case "sales_policy": popup(URL, 430, 650, name); break;
				case "contact_us_map": windowless_popup(URL, "auto", "auto", 243, 500); break;
				case "admin_payment_info": popup(URL, 400, 300, 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) {
		//alert("set button " + name + " to " + state); // debug
		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";
		}
	}
	

// 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 click_pagination_link(start) {
		location = update_query_string("start", start);
	}
	
	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 = "";
	}
	
