/*
 * create_element (name) -- create an element of type name
 * Works even when the document is sent as pure XML, and in IE6 too!
 */
var		create_element = null;

//if (typeof(document.createElementNS) == "function" && document.contentType == "application/xhtml+xml") {
if (typeof(document.createElementNS) == "function" && document.documentElement.namespaceURI)
	create_element = function (name) { return document.createElementNS(document.documentElement.namespaceURI, name); };
else
	create_element = function (name) { return document.createElement(name); }

/*
 * $() -- lookup element(s) by ID (shamelessly lifted from Prototype)
 */
function $() {
  var elements = new Array();

  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1)
      return element;

    elements.push(element);
  }

  return elements;
}

/*
 * register_onload_event (function) -- register a function to be called when the document is loaded
 * You may register as many events as you like!
 */
function	register_onload_event (cb)
{
	var		curr = window.onload;

	if (curr)
		window.onload = function () { curr(); cb(); }
	else
		window.onload = cb;
}

/*
 * get_child_by_tag_name (parent, tag_name)
 * -- Return the first child of parent with specified tag name
 */
function	get_child_by_tag_name (parent, tag_name)
{
	var		children = parent.getElementsByTagName(tag_name);

	if (!children || children.length < 1)
		return null;
	
	return children[0];
}

/*
 * get_child_data (parent, tag_name)
 * Return the data of the first child of the first child of parent with "tag_name"
 */
function	get_child_data (parent, tag_name)
{
	var		child = get_child_by_tag_name(parent, tag_name);
	return child && child.firstChild ? child.firstChild.data : null;
}

/*
 * has_child_data (elem)
 * Return true if elem's first child is a node with data
 */
function	has_child_data (elem)
{
	return elem.firstChild && elem.firstChild.data;
}

/*
 * remove_all_children (parent) -- remove all child nodes en masse
 */
function	remove_all_children (parent)
{
	while (parent.hasChildNodes())
		parent.removeChild(parent.firstChild);
}

/*
 * add_class (element, className) -- add className to the list of CSS classes for element
 */
function add_class (element, className)
{
	if (!has_class(element, className)) {
		if (element.className) element.className += " " + className;
		else element.className = className;
	}
}

/*
 * remove_class (element, className) -- remove className from the list of CSS classes for element
 */
function remove_class (element, className)
{
	var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)");
	element.className = element.className.replace(regexp, "$2");
}

/*
 * has_class (element, className) -- return true if element has className as one of its CSS classes
 */
function has_class (element, className)
{
	var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)");
	return regexp.test(element.className);
}

/*
 * replace_class (element, oldClassName, newClassName)
 * -- remove oldClassName from element's class list and add newClassName
 */
function replace_class (element, oldClassName, newClassName)
{
	if (oldClassName == newClassName)
		return;

	if (has_class(element, newClassName)) {
		remove_class(element, oldClassName);
		return;
	}

	if (has_class(element, oldClassName)) {
		var regexp = new RegExp("(^|\\s)" + oldClassName + "(\\s|$)");
		element.className = element.className.replace(regexp, newClassName);
	} else {
		if (element.className) element.className += " " + newClassName;
		else element.className = newClassName;
	}
}

/*
 * mem_fun (class_obj, member_function)
 * -- Returns a function, that when invoked, calls class_obj.member_function() with its arguments
 */
function mem_fun (class_obj, member_function)
{
	return function () { return member_function.apply(class_obj, arguments); }
}

/*
 * mem_fun_this (class_obj, member_function)
 * -- Returns a function, that when invoked, calls class_obj.member_function() with this as 1st arg
 */
function mem_fun_this (class_obj, member_function)
{
	return function () { return member_function.apply(class_obj, [ this ]); }
}

/*
 * for_each (list, function) -- Invoke function() for each item in list
 */
function for_each (list, fun)
{
	for (var i = 0; i < list.length; ++i)
		fun.apply(list[i], []);
}

/*
 * set_cookie (name, value, days)
 */
function set_cookie (name, value, days) {
	var		expires = "";
	if (days) {
		var	date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		expires = "; expires=" + date.toGMTString();
	}
	document.cookie = name + "=" + value + expires + "; path=/";
}

/*
 * get_cookie (name)
 */
function get_cookie (name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

/*
 * unset_cookie (name)
 */
function unset_cookie(name) {
	set_cookie(name, "", -1);
}

/*
 * serialize_form (form)
 * -- Returns a application/x-www-form-urlencoded string (i.e. query string) for specified form
 */
function serialize_form (form)
{
	/*
	 * the following code is lifted (legally) from Prototype 1.4.0 
	 * http://prototype.conio.net * (c) 2005 Sam Stephenson <sam@conio.net>
	 */
	var	form_element_serializers = {
	  input: function(element) {
	    switch (element.type.toLowerCase()) {
	//      case 'submit':
	      case 'hidden':
	      case 'password':
	      case 'text':
		return form_element_serializers.textarea(element);
	      case 'checkbox':
	      case 'radio':
		return form_element_serializers.inputSelector(element);
	    }
	    return null;
	  },

	  inputSelector: function(element) {
	    if (element.checked)
	      return [element.value];
	    return null;
	  },

	  textarea: function(element) {
	    return [element.value];
	  },

	  select: function(element) {
	    return form_element_serializers[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element);
	  },

	  selectOne: function(element) {
	    var value = '', opt, index = element.selectedIndex;
	    if (index >= 0) {
	      opt = element.options[index];
	      value = opt.value;
	      //if (!value && !('value' in opt))
	      if (!value)
		value = opt.text;
	    }
	    return [value];
	  },

	  selectMany: function(element) {
	    var value = new Array();
	    for (var i = 0; i < element.length; i++) {
	      var opt = element.options[i];
	      if (opt.selected) {
		var optValue = opt.value;
		//if (!optValue && !('value' in opt))
		if (!optValue)
		  optValue = opt.text;
		value.push(optValue);
	      }
	    }
	    return value;
	  }
	}
	function serialize_form_element (elem)
	{
		var	serializer = form_element_serializers[elem.tagName.toLowerCase()];
		var	serialized_string = "";

		if (serializer && !elem.disabled) {
			var	name = encodeURIComponent(elem.name);
			var	value = serializer(elem);

			if (value) {
				for (var i = 0; i < value.length; ++i) {
					if (serialized_string.length > 0)
						serialized_string += "&";

					serialized_string += name;
					serialized_string += "=";
					serialized_string += encodeURIComponent(value[i]);
				}
			}
		}

		return serialized_string;
	}

	var	serialized_string = "";

	for (var i = 0; i < form.elements.length; ++i) {
		var str = serialize_form_element(form.elements[i]);

		if (serialized_string.length > 0 && str.length > 0)
			serialized_string += "&";
		serialized_string += str;
	}

	return serialized_string;
}

/* 
 * class ajax_requester - wrapper around the ubiquitous XMLHttpRequest object (aka AJAX!)
 */
function ajax_requester (_callback, _error_callback)
{
	/*
	 * PRIVATE MEMBERS
	 */
	var	_this = this;
	var	xmlhttp = null;

	function xmlhttp_callback ()
	{
		if (xmlhttp && xmlhttp.readyState == 4) {
			if (xmlhttp.status != 200 && xmlhttp.status != 0) {
				// HTTP error
				if (_this.error_callback)
					_this.error_callback(xmlhttp.status, xmlhttp.statusText);
			} else if (!xmlhttp.responseXML || !xmlhttp.responseXML.documentElement) {
				// Above: the pointless check for documentElement is an IE hack
				// XML error
				if (_this.error_callback)
					_this.error_callback(-1, "Response not XML: " + xmlhttp.responseText);
			} else
				// We're good!
				_this.callback(xmlhttp.responseXML);

			// We won't be needing these again!
			//xmlhttp.onreadystatechange = null;
			_this._cleanup(xmlhttp);
			xmlhttp = null;
		}
	}

	function init_xmlhttp (method, uri)
	{
		if (xmlhttp)
			// Ignore any response from the existing XML/HTTP instance
			//xmlhttp.onreadystatechange = null;
			_this._cleanup(xmlhttp);

		xmlhttp = _this._get_native();
		xmlhttp.open(method, uri);

		if (typeof(xmlhttp.setRequestHeader) == "function")
			xmlhttp.setRequestHeader("If-Modified-Since", "Wed, 15 Nov 1995 00:00:00 GMT");

		xmlhttp.onreadystatechange = xmlhttp_callback;
	}

	/*
	 * PUBLIC MEMBERS
	 */

	/*
	 * callback(document)
	 *  Called when the response has been fully received.
	 *  Takes as argument the XML document DOM node representing the response.
	 */
	this.callback = _callback;

	/*
	 * error_callback(status_code, status_string)
	 *  Called upon an error, if non-null.
	 *  Takes as arguments a status code and a status string.
	 */
	if (arguments.length >= 2)
		this.error_callback = _error_callback;

	/*
	 * get(uri)
	 *  Send a GET request to the specified URI
	 */
	this.get = function (uri) {
		init_xmlhttp("GET", uri);
		xmlhttp.send(null);
	}

	/*
	 * post(uri, content_type, data)
	 *  Send a POST request with the specified data to the specified URI
	 */
	this.post = function (uri, content_type, data) {
		init_xmlhttp("POST", uri);
		xmlhttp.setRequestHeader("Content-Type", content_type);
		xmlhttp.send(data);
	}
}

/*
 * ajax_requester.prototype.error_callback(status_code, status_string)
 *  Default error handler for new ajax_requester objects
 */
ajax_requester.prototype.error_callback = function (status_code, status_string) {
	alert("XML/HTTP: " + status_code + " " + status_string);
}

/*
 * ajax_requester.prototype.not_supported()
 *  Called if the web browser does not support AJAX
 */
ajax_requester.prototype.not_supported = function () {
	alert("Your web browser does not support XML/HTTP and thus cannot browse this site.");
}

/*
 * Implementation details for ajax_requester - DO NOT TOUCH
 */
ajax_requester.prototype._get_native = null;
/*@cc_on
@if (@_jscript_version >= 5)
	ajax_requester.prototype._get_native = function () {
		var	_xmlhttp;
		try {
			_xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				_xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (E) {
				_xmlhttp = null;
				this.not_supported();
			}
		}
		return _xmlhttp;
	}

	ajax_requester.prototype._cleanup = function () { }
@end @*/
if (!ajax_requester.prototype._get_native) {
	if (typeof XMLHttpRequest != 'undefined') {
		ajax_requester.prototype._get_native = function () {
			var	_xmlhttp;
			try {
				_xmlhttp = new XMLHttpRequest();
			} catch (e) {
				_xmlhttp = null;
				this.not_supported();
			}
			return _xmlhttp;
		};

		ajax_requester.prototype._cleanup = function (_xmlhttp) { _xmlhttp.onreadystatechange = null; };
	} else
		ajax_requester.prototype._get_native = function () { this.not_supported(); };
}
// End of AJAX implementation details


/*
 * class vs_xmlhttp - compatability wrapper around new ajax_requester
 */
function	vs_xmlhttp (uri, callback)
{
	function callback_wrapper (xmldoc)
	{
		if (xmldoc.documentElement.tagName == "error") {
			alert("Error: " + xmldoc.documentElement.firstChild.data);
			callback(null);
		} else if (xmldoc.documentElement.tagName == "parsererror") {
			alert("XML Parser Error: " + xmldoc.documentElement.firstChild.data);
			callback(null);
		} else
			callback(xmldoc.documentElement);
	}
	function error_callback (status, statusText)
	{
		if (status < 0)
			alert("The XML/HTTP response is null.  Please try again. (1)");
		else
			alert("XML/HTTP: " + status + " " + statusText);
		callback(null);
	}

	var	ajax = new ajax_requester(callback_wrapper, error_callback);

	this.get = function () { ajax.get(uri); }
	this.post = function (content_type, data) { ajax.post(uri, content_type, data); }
}
