// Client stub for the Application PHP Class
function Application(callback) {
	mode = 'sync';
	if (callback) { mode = 'async'; }
	this.className = 'Application';
	this.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'/ClientServer.php?','JSON');
}
Application.prototype  = {
	Sync: function() { this.dispatcher.Sync(); }, 
	Async: function(callback) { this.dispatcher.Async(callback); },
	getPage: function() { return this.dispatcher.doCall('getPage',arguments); }
}

// Client stub for the test PHP Class
function test(callback) {
	mode = 'sync';
	if (callback) { mode = 'async'; }
	this.className = 'test';
	this.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'/ClientServer.php?','JSON');
}
test.prototype  = {
	Sync: function() { this.dispatcher.Sync(); }, 
	Async: function(callback) { this.dispatcher.Async(callback); },
	saveResult: function() { return this.dispatcher.doCall('saveResult',arguments); }
}

// Client stub for the respondent PHP Class
function respondent(callback) {
	mode = 'sync';
	if (callback) { mode = 'async'; }
	this.className = 'respondent';
	this.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'/ClientServer.php?','JSON');
}
respondent.prototype  = {
	Sync: function() { this.dispatcher.Sync(); }, 
	Async: function(callback) { this.dispatcher.Async(callback); },
	userExists: function() { return this.dispatcher.doCall('userExists',arguments); },
	getCityes: function() { return this.dispatcher.doCall('getCityes',arguments); },
	getCabinete: function() { return this.dispatcher.doCall('getCabinete',arguments); },
	remind_pass: function() { return this.dispatcher.doCall('remind_pass',arguments); }
}

// Compat.js
/**
 * Compat functions
 * @category	HTML
 * @package	AJAX
 * @author	Joshua Eichorn <josh@bluga.net>
 * @copyright	2005 Joshua Eichorn
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */
/**
 *  Functions for compatibility with older browsers
 */
if (!String.fromCharCode && !String.prototype.fromCharCode) {
	String.prototype.fromCharCode = function(code)
	{
		var h = code.toString(16);
		if (h.length == 1) {
			h = '0' + h;
		}
		return unescape('%' + h);
	}
}
if (!String.charCodeAt && !String.prototype.charCodeAt) {
	String.prototype.charCodeAt = function(index)
	{
		var c = this.charAt(index);
		for (i = 1; i < 256; i++) {
			if (String.fromCharCode(i) == c) {
				return i;
			}
		}
	}
}
// http://www.crockford.com/javascript/remedial.html
if (!Array.splice && !Array.prototype.splice) {
	Array.prototype.splice = function(s, d)
	{
		var max = Math.max,
		min = Math.min,
		a = [], // The return value array
		e,  // element
		i = max(arguments.length - 2, 0),   // insert count
		k = 0,
		l = this.length,
		n,  // new length
		v,  // delta
		x;  // shift count

		s = s || 0;
		if (s < 0) {
			s += l;
		}
		s = max(min(s, l), 0);  // start point
		d = max(min(typeof d == 'number' ? d : l, l - s), 0);	// delete count
		v = i - d;
		n = l + v;
		while (k < d) {
			e = this[s + k];
			if (!e) {
				a[k] = e;
			}
			k += 1;
		}
		x = l - s - d;
		if (v < 0) {
			k = s + i;
			while (x) {
				this[k] = this[k - v];
				k += 1;
				x -= 1;
			}
			this.length = n;
		} else if (v > 0) {
			k = 1;
			while (x) {
				this[n - k] = this[l - k];
				k += 1;
				x -= 1;
			}
		}
		for (k = 0; k < i; ++k) {
			this[s + k] = arguments[k + 2];
		}
		return a;
	}
}
if (!Array.push && !Array.prototype.push) {
	Array.prototype.push = function()
	{
		for (var i = 0, startLength = this.length; i < arguments.length; i++) {
			this[startLength + i] = arguments[i];
		}
		return this.length;
	}
}
if (!Array.pop && !Array.prototype.pop) {
	Array.prototype.pop = function()
	{
		return this.splice(this.length - 1, 1)[0];
	}
}
/*
	From IE7, version 0.9 (alpha) (2005-08-19)
	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
*/
if (!DOMParser.parseFromString && window.ActiveXObject)
{
function DOMParser() {/* empty constructor */};
DOMParser.prototype = {
	parseFromString: function(str, contentType) {
		var xmlDocument = new ActiveXObject('Microsoft.XMLDOM');
		xmlDocument.loadXML(str);
		return xmlDocument;
	}
};

function XMLSerializer() {/* empty constructor */};
XMLSerializer.prototype = {
	serializeToString: function(root) {
		return root.xml || root.outerHTML;
	}
};
}
// Main.js
/**
 * JavaScript library for use with HTML_AJAX
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category	HTML
 * @package	Ajax
 * @author	Joshua Eichorn <josh@bluga.net>
 * @author	Arpad Ray <arpad@php.net>
 * @author	David Coallier <davidc@php.net>
 * @author	Elizabeth Smith <auroraeosrose@gmail.com>
 * @copyright	2005 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */

/**
 * HTML_AJAX static methods, this is the main proxyless api, it also handles global error and event handling
 */
var HTML_AJAX = {
	version: '0.5.2',
	defaultServerUrl: false,
	defaultEncoding: 'JSON',
	queues: false,
	clientPools: {},
	// get an HttpClient, supply a name to use the pool of that name or the default if it isn't found
	httpClient: function(name) {
		if (name) {
			if (this.clientPools[name]) {
				return this.clientPools[name].getClient();
			}
		}
		return this.clientPools['default'].getClient();
	},
	// Pushing the given request to queue specified by it, in default operation this will immediately make a request
	// request might be delayed or never happen depending on the queue setup
	// making a sync request to a non immediate queue will cause you problems so just don't do it
	makeRequest: function(request) {
		if (!HTML_AJAX.queues[request.queue]) {
			var e = new Error('Unknown Queue: '+request.queue);
			if (HTML_AJAX.onError) {
				HTML_AJAX.onError(e);
				return false;
			}
			else {
				throw(e);
			}
		}
		else {
			var qn = request.queue;
			var q = HTML_AJAX.queues[qn];

			HTML_AJAX.queues[request.queue].addRequest(request);
			return HTML_AJAX.queues[request.queue].processRequest();
		}
	},
	// get a serializer object for a specific encoding
	serializerForEncoding: function(encoding) {
		for(var i in HTML_AJAX.contentTypeMap) {
			if (encoding == HTML_AJAX.contentTypeMap[i] || encoding == i) {
				return eval("new HTML_AJAX_Serialize_"+i+";");
			}
		}
		return new HTML_AJAX_Serialize_Null();
	},
	fullcall: function(url,encoding,className,method,callback,args, options) {
		var serializer = HTML_AJAX.serializerForEncoding(encoding);

		var request = new HTML_AJAX_Request(serializer);
		if (callback) {
			request.isAsync = true;
		}
		request.requestUrl = url;
		request.className = className;
		request.methodName = method;
		request.callback = callback;
		request.args = args;
		if (options) {
			for(var i in options) {
				request[i] = options[i];
			}
			if (options.grab) {
				if (!request.args || !request.args.length) {
					request.requestType = 'GET';
				}
			}
		}

		return HTML_AJAX.makeRequest(request);
	},
	callPhpCallback: function(phpCallback, jsCallback, url) {
		var args = new Array();
		for (var i = 3; i < arguments.length; i++) {
			args.push(arguments[i]);
		}
		if (HTML_AJAX_Util.getType(phpCallback[0]) == 'object') {
			jsCallback(phpCallback[0][phpCallback[1]](args));
			return;
		}
		if (!url) {
			url = HTML_AJAX.defaultServerUrl;
		}
		HTML_AJAX.fullcall(url, HTML_AJAX.defaultEncoding,
			false, false, jsCallback, args, {phpCallback: phpCallback});
	},
	call: function(className,method,callback) {
		var args = new Array();
		for(var i = 3; i < arguments.length; i++) {
			args.push(arguments[i]);
		}
		return HTML_AJAX.fullcall(HTML_AJAX.defaultServerUrl,HTML_AJAX.defaultEncoding,className,method,callback,args);
	},
	grab: function(url,callback,options) {
		if (!options) {
			options = {grab:true};
		}
		else {
			options['grab'] = true;
		}
		return HTML_AJAX.fullcall(url,'Null',false,null,callback, '', options);
	},
	post: function(url,payload,callback,options) {
		var serializer = 'Null';
		if (HTML_AJAX_Util.getType(payload) == 'object') {
			serializer = 'Urlencoded';
		}
		return HTML_AJAX.fullcall(url,serializer,false,null,callback, payload, options);
	},
	replace: function(id) {
		var callback = function(result) {
			HTML_AJAX_Util.setInnerHTML(HTML_AJAX_Util.getElement(id),result);
		}
		if (arguments.length == 2) {
			// grab replacement
			HTML_AJAX.grab(arguments[1],callback);
		}
		else {
			// call replacement
			var args = new Array();
			for(var i = 3; i < arguments.length; i++) {
				args.push(arguments[i]);
			}
			HTML_AJAX.fullcall(HTML_AJAX.defaultServerUrl,HTML_AJAX.defaultEncoding,arguments[1],arguments[2],callback,args, {grab:true});
		}
	},
	append: function(id) {
		var callback = function(result) {
			HTML_AJAX_Util.setInnerHTML(HTML_AJAX_Util.getElement(id),result,'append');
		}
		if (arguments.length == 2) {
			// grab replacement
			HTML_AJAX.grab(arguments[1],callback);
		}
		else {
			// call replacement
			var args = new Array();
			for(var i = 3; i < arguments.length; i++) {
				args.push(arguments[i]);
			}
			HTML_AJAX.fullcall(HTML_AJAX.defaultServerUrl,HTML_AJAX.defaultEncoding,arguments[1],arguments[2],callback,args, {grab:true});
		}
	},
	// override to add top level loading notification (start)
	Open: function(request) {
	},
	// override to add top level loading notification (finish)
	Load: function(request) {
	},
	/*
	// A really basic error handler
	onError: function(e) {
		msg = "";
		for(var i in e) {
			msg += i+':'+e[i]+"\n";
		}
		alert(msg);
	},
	*/
	// Class postfix to content-type map
	contentTypeMap: {
		'JSON':			'application/json',
		'Null':			'text/plain',
		'Error':		'application/error',
		'PHP':			'application/php-serialized',
		'HA' :			'application/html_ajax_action',
		'Urlencoded':	'application/x-www-form-urlencoded'
	},
	// used internally to make queues work, override Load or onError to perform custom events when a request is complete
	// fires on success and error
	requestComplete: function(request,error) {
		for(var i in HTML_AJAX.queues) {
			if (HTML_AJAX.queues[i].requestComplete) {
				HTML_AJAX.queues[i].requestComplete(request,error);
			}
		}
	},

	// turns a form into a urlencoded string
	formEncode: function(form, array_format) {
		form = HTML_AJAX_Util.getElement(form);
		var el, inpType, value, name;
		var out = (array_format) ? {} : '';
		var inputTags = form.getElementsByTagName('INPUT');
		var selectTags = form.getElementsByTagName('SELECT');
		var buttonTags = form.getElementsByTagName('BUTTON');
		var textareaTags = form.getElementsByTagName('TEXTAREA');
		var arrayRegex = /(.+)%5B%5D/;
		var AssocArrayRegex = /(.+)%5B(.+)%5D/;
		var Assoc2ArrayRegex = /(.+)%5B(.+)%5D%5B(.+)%5D/;
		var Assoc3ArrayRegex = /(.+)%5B(.+)%5D%5B(.+)%5D%5B(.+)%5D/;

		var validElement = function (element) {
			if (!element || !element.getAttribute) {
				return false;
			}
			el = element;
			name = HTML_AJAX_Util.encodeUrl(el.getAttribute('name'));

			if (!name) {
				// no element name so skip
				return false;
			}
			if (element.disabled) {
				return false;
			}

			if (!array_format) {
				value = HTML_AJAX_Util.encodeUrl(el.value);
			} else {
				value = el.value;
			}

			inpType = el.getAttribute('type');
			return true;
		}

		inputLoop:
		for (var i=0; i < inputTags.length; i++) {
			if (!validElement(inputTags[i])) {
				continue;
			}
			if (inpType == 'checkbox' || inpType == 'radio') {
				if (!el.checked) {
					// unchecked radios/checkboxes don't get submitted
					continue inputLoop;
				}
				var arr_var = arrayRegex.exec(name);
				if (array_format && arr_var) {
					if (!out[arr_var[1]]) {
						out[arr_var[1]] = new Array();
					}
					out[arr_var[1]].push(value);
					continue inputLoop;
				}
				arr_var = AssocArrayRegex.exec(name);
				if (array_format && arr_var) {
					if (!out[arr_var[1]]) {
						out[arr_var[1]] = {};
					}
					out[arr_var[1]][arr_var[2]] = value;
					continue inputLoop;
				}
			}else{
				/*var arr_var = arrayRegex.exec(name);
				if (array_format && arr_var) {
					if (!out[arr_var[1]]) {
						out[arr_var[1]] = new Array();
					}
					out[arr_var[1]].push(value);
					continue inputLoop;
				}
				arr_var = AssocArrayRegex.exec(name);
				if (array_format && arr_var) {
					if (!out[arr_var[1]]) {
						out[arr_var[1]] = {};
					}
					out[arr_var[1]][arr_var[2]] = value;
					continue inputLoop;
				}  */

				arr_var2 = Assoc2ArrayRegex.exec(name);
				arr_var3 = Assoc3ArrayRegex.exec(name);
				arr_var = AssocArrayRegex.exec(name);
				if (arr_var3) {
					if (!out[arr_var3[1]]) {
						out[arr_var3[1]] = {};
					}
					if (!out[arr_var3[1]][arr_var3[2]]) {
						out[arr_var3[1]][arr_var3[2]] = {};
					}
					if (!out[arr_var3[1]][arr_var3[2]][arr_var3[3]]) {
						out[arr_var3[1]][arr_var3[2]][arr_var3[3]] = {};
					}
					out[arr_var3[1]][arr_var3[2]][arr_var3[3]][arr_var3[4]] = value;
					continue inputLoop;
				}else if (arr_var2) {
					if (!out[arr_var2[1]]) {
						out[arr_var2[1]] = {};
					}
					if (!out[arr_var2[1]][arr_var2[2]]) {
						out[arr_var2[1]][arr_var2[2]] = {};
					}
					out[arr_var2[1]][arr_var2[2]][arr_var2[3]] = value;
					continue inputLoop;
				}else if (arr_var) {
					if (!out[arr_var[1]]) {
						out[arr_var[1]] = {};
					}
					out[arr_var[1]][arr_var[2]] = value;
					continue inputLoop;
				}else{
					out[name] = value;
					continue inputLoop;
				}
			}
			// add element to output array
			if (array_format) {
				out[name] = value;
			} else {
				out += name + '=' + value + '&';
			}
		} // end inputLoop


		selectLoop:
		for (var i=0; i<selectTags.length; i++) {
			if (!validElement(selectTags[i])) {
				continue selectLoop;
			}
			var arr_var = arrayRegex.exec(name);
			if(arr_var)
				name = arr_var[1];
			var options = el.options;
			for (var z=0; z<options.length; z++){
				var option=options[z];
				if(option.selected){
					if (array_format) {
						arr_var2 = Assoc2ArrayRegex.exec(name);
						arr_var3 = Assoc3ArrayRegex.exec(name);
						arr_var = AssocArrayRegex.exec(name);
						if (arr_var3) {
							if (el.type == 'select-one') {
								if (!out[arr_var3[1]]) {
									out[arr_var3[1]] = {};
								}
								if (!out[arr_var3[1]][arr_var3[2]]) {
									out[arr_var3[1]][arr_var3[2]] = {};
								}
								if (!out[arr_var3[1]][arr_var3[2]][arr_var3[3]]) {
									out[arr_var3[1]][arr_var3[2]][arr_var3[3]] = {};
								}
								out[arr_var3[1]][arr_var3[2]][arr_var3[3]][arr_var3[4]] = option.value;
								//only one item can be selected
								continue selectLoop;
							}else{
								if (!out[arr_var3[1]]) {
									out[arr_var3[1]] = {};
								}
								out[arr_var3[1]][arr_var3[2]] = option.value;
								continue selectLoop;
							}
						}else if (arr_var2) {
							if (el.type == 'select-one') {
								if (!out[arr_var2[1]]) {
									out[arr_var2[1]] = {};
								}
								if (!out[arr_var2[1]][arr_var2[2]]) {
									out[arr_var2[1]][arr_var2[2]] = {};
								}
								out[arr_var2[1]][arr_var2[2]][arr_var2[3]] = option.value;
								//only one item can be selected
								continue selectLoop;
							}else{
								if (!out[arr_var2[1]]) {
									out[arr_var2[1]] = {};
								}
								out[arr_var2[1]][arr_var2[2]] = option.value;
								continue selectLoop;
							}
						}else if (arr_var) {
							if (el.type == 'select-one') {
								if (!out[arr_var[1]]) {
									out[arr_var[1]] = {};
								}
								out[arr_var[1]][arr_var[2]] = option.value;
								//only one item can be selected
								continue selectLoop;
							}else{
								if (!out[arr_var[1]]) {
									out[arr_var[1]] = {};
								}
								out[arr_var[1]][arr_var[2]] = option.value;
								continue selectLoop;
							}
						}else{
							if (el.type == 'select-one') {
								out[name] = option.value;
								//only one item can be selected
								continue selectLoop;
							} else {
								if (!out[name]) {
									out[name] = new Array();
								}
								out[name].push(option.value);
							}
						}
					} else {
						out += name + '=' + option.value + '&';
						if (el.type == 'select-one') {
							continue selectLoop;
						}
					}
				}
			}
		} // end selectLoop

		buttonLoop:
		for (var i=0; i<buttonTags.length; i++) {
			if (!validElement(buttonTags[i])) {
				continue;
			}
			// add element to output array
			if (array_format) {
				out[name] = value;
			} else {
				out += name + '=' + value + '&';
			}
		} // end buttonLoop

		textareaLoop:
		for (var i=0; i<textareaTags.length; i++) {
			if (!validElement(textareaTags[i])) {
				continue;
			}
			// add element to output array
			if (array_format) {
				out[name] = value;
			} else {
				out += name + '=' + value + '&';
			}
		} // end textareaLoop

		return out;
	},
	// submits a form through ajax. both arguments can be either DOM nodes or IDs, if the target is omitted then the form is set to be the target
	formSubmit: function (form, target, options)
	{
		form = HTML_AJAX_Util.getElement(form);
		if (!form) {
		// let the submit be processed normally
			return false;
		}

		var out = HTML_AJAX.formEncode(form);
		target = HTML_AJAX_Util.getElement(target);
		if (!target) {
			target = form;
		}
		try
		{
			var action = form.attributes['action'].value;
		}
		catch(e){}
		if(action == undefined)
		{
			action = form.getAttribute('action');
		}

		var callback = false;
		if (HTML_AJAX_Util.getType(target) == 'function') {
			callback = target;
		}
		else {
			callback = function(result) {
				// result will be undefined if HA_Action is returned, so skip the replace
				if (typeof result != 'undefined') {
					HTML_AJAX_Util.setInnerHTML(target,result);
				}
			}
		}

		var serializer = HTML_AJAX.serializerForEncoding('Null');
		var request = new HTML_AJAX_Request(serializer);
		request.isAsync = true;
		request.callback = callback;

		switch (form.getAttribute('method').toLowerCase()) {
		case 'post':
			var headers = {};
			headers['Content-Type'] = 'application/x-www-form-urlencoded';
			request.customHeaders = headers;
			request.requestType = 'POST';
			request.requestUrl = action;
			request.args = out;
			break;
		default:
			if (action.indexOf('?') == -1) {
				out = '?' + out.substr(0, out.length - 1);
			}
			request.requestUrl = action+out;
			request.requestType = 'GET';
		}

		if(options) {
			for(var i in options) {
				request[i] = options[i];
			}
		}
		HTML_AJAX.makeRequest(request);
		return true;
	}, // end formSubmit()
	makeFormAJAX: function(form,target,options) {
		form = HTML_AJAX_Util.getElement(form);
		var preSubmit = false;
		if(typeof form.onsubmit != 'undefined') {
			preSubmit = form.onsubmit;
			form.onsubmit = function() {};
		}
		form.HAOptions = options;
		var handler = function(e) {
			var form = HTML_AJAX_Util.eventTarget(e);

			var valid = true;
			if (preSubmit) {
				valid = preSubmit();
			}
			if (valid) {
				HTML_AJAX.formSubmit(form,target,form.HAOptions);
			}
			// cancel submission in IE
			e.returnValue = false;
			// cancel submission in FF
			if (e.preventDefault) {
				e.preventDefault();
			}
		}
		HTML_AJAX_Util.registerEvent(form,'submit',handler);
	}
}




// small classes that I don't want to put in there own file

function HTML_AJAX_Serialize_Null() {}
HTML_AJAX_Serialize_Null.prototype = {
	contentType: 'text/plain; charset=utf-8',
	serialize: function(input) {
		return new String(input).valueOf();
	},

	unserialize: function(input) {
		return new String(input).valueOf();
	}
}

function HTML_AJAX_Serialize_XML() {}
HTML_AJAX_Serialize_XML.prototype = {
	contentType: 'application/xml; charset=utf-8',
	serialize: function(input) {
		var xml = '';
		if(typeof(input) == 'object' && input)
		{
			for (var i = 0;i<input.length;i++)
			{
				xml += new XMLSerializer().serializeToString(input[i]);
			}
		}
		return xml;
	},

	unserialize: function(input) {
		return input;
	}
}

// serialization class for JSON, wrapper for JSON.stringify in json.js
function HTML_AJAX_Serialize_JSON() {}
HTML_AJAX_Serialize_JSON.prototype = {
	contentType: 'application/json; charset=utf-8',
	serialize: function(input) {
		return HTML_AJAX_JSON.stringify(input);
	},
	unserialize: function(input) {
		try {
			return eval('('+input+')');
		} catch(e) {
			// sometimes JSON encoded input isn't created properly, if eval of it fails we use the more forgiving but slower parser so will at least get something
			return HTML_AJAX_JSON.parse(input);
		}
	}
}

function HTML_AJAX_Serialize_Error() {}
HTML_AJAX_Serialize_Error.prototype = {
	contentType: 'application/error; charset=utf-8',
	serialize: function(input) {
		var ser = new HTML_AJAX_Serialize_JSON();
		return ser.serialize(input);
	},
	unserialize: function(input) {
		var ser = new HTML_AJAX_Serialize_JSON();
		var data = new ser.unserialize(input);

		var e = new Error('PHP Error: '+data.errStr);
		for(var i in data) {
			e[i] = data[i];
		}
		throw e;
	}
}

// Processing Queues

// simple queue, just processes the request immediately
function HTML_AJAX_Queue_Immediate() {}
HTML_AJAX_Queue_Immediate.prototype = {
	request: false,
	addRequest: function(request) {
		this.request = request;
	},
	processRequest: function() {
		var client = HTML_AJAX.httpClient();
		client.request = this.request;
		return client.makeRequest();
	}
}



// create a default queue, has to happen after the Queue class has been defined
HTML_AJAX.queues = new Object();
HTML_AJAX.queues['default'] = new HTML_AJAX_Queue_Immediate();

// Queue.js
/**
 * Various processing queues, use when you want to control how multiple requests are made
 * @category	HTML
 * @package	AJAX
 * @author	Joshua Eichorn <josh@bluga.net>
 * @copyright	2005 Joshua Eichorn
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */

// Single Buffer queue with interval
// works by attempting to send a request every x miliseconds
// if an item is currently in the queue when a new item is added it will be replaced
// simple queue, just processes the request immediately
// the first request starts the interval timer
function HTML_AJAX_Queue_Interval_SingleBuffer(interval,singleOutstandingRequest) {
	this.interval = interval;
	if (singleOutstandingRequest) {
		this.singleOutstandingRequest = true;
	}
}
HTML_AJAX_Queue_Interval_SingleBuffer.prototype = {
	request: false,
	_intervalId: false,
	singleOutstandingRequest: false,
	client: false,
	addRequest: function(request) {
		this.request = request;
	},
	processRequest: function() {
		if (!this._intervalId) {
			this.runInterval();
			this.start();
		}
	},
	start: function() {
		var self = this;
		this._intervalId = setInterval(function() { self.runInterval() },this.interval);
	},
	stop: function() {
		clearInterval(this._intervalId);
	},
	runInterval: function() {
		if (this.request) {
			if (this.singleOutstandingRequest && this.client) {
				this.client.abort();
			}
			this.client = HTML_AJAX.httpClient();
			this.client.request = this.request;
			this.request = false;
			this.client.makeRequest();
		}
	}
}

// Requests return in the same order they were called
// this helps handle high latency situations
function HTML_AJAX_Queue_Ordered() { }
HTML_AJAX_Queue_Ordered.prototype = {
	request: false,
	order: 0,
	current: 0,
	callbacks: {},
	interned: {},
	addRequest: function(request) {
		request.order = this.order;
		this.request = request;
		this.callbacks[this.order] = this.request.callback;
		var self = this;
		this.request.callback = function(result) {
			self.processCallback(result,request.order);
		}
	},
	processRequest: function() {
		var client = HTML_AJAX.httpClient();
		client.request = this.request;
		client.makeRequest();
		this.order++;
	},
	requestComplete: function(request,e) {
		// something when wrong with the request lets stop waiting for it
		if (e) {
			this.current++;
		}
	},
	processCallback: function(result,order) {
		if (order == this.current) {
			this.callbacks[order](result);
			this.current++;
		}
		else {
			this.interned[order] = result;
		}
		while (this.interned[this.current]) {
			this.callbacks[this.current](this.interned[this.current]);
			this.current++;
		}
}
}

// Make a single request at once, canceling and currently outstanding requests when a new one is made
function HTML_AJAX_Queue_Single() {
}
HTML_AJAX_Queue_Single.prototype = {
	request: false,
	client: false,
	addRequest: function(request) {
		this.request = request;
	},
	processRequest: function() {
		if (this.request) {
			if (this.client) {
				this.client.abort();
			}
			this.client = HTML_AJAX.httpClient();
			this.client.request = this.request;
			this.request = false;
			this.client.makeRequest();
		}
	}
}

/**
 * Priority queue
 *
 * @author	 Arpad Ray <arpad@php.net>
 */
function HTML_AJAX_Queue_Priority_Item(item, time) {
	this.item = item;
	this.time = time;
}
HTML_AJAX_Queue_Priority_Item.prototype = {
	compareTo: function (other) {
		var ret = this.item.compareTo(other.item);
		if (ret == 0) {
			ret = this.time - other.time;
		}
		return ret;
	}
}

function HTML_AJAX_Queue_Priority_Simple(interval) {
	this.interval = interval;
	this.idleMax = 10;			// keep the interval going with an empty queue for 10 intervals
	this.requestTimeout = 5;	  // retry uncompleted requests after 5 seconds
	this.checkRetryChance = 0.1;  // check for uncompleted requests to retry on 10% of intervals
	this._intervalId = 0;
	this._requests = [];
	this._removed = [];
	this._len = 0;
	this._removedLen = 0;
	this._idle = 0;
}
HTML_AJAX_Queue_Priority_Simple.prototype = {
	isEmpty: function () {
		return this._len == 0;
	},
	addRequest: function (request) {
		request = new HTML_AJAX_Queue_Priority_Item(request, new Date().getTime());
		++this._len;
		if (this.isEmpty()) {
			this._requests[0] = request;
			return;
		}
		for (i = 0; i < this._len - 1; i++) {
			if (request.compareTo(this._requests[i]) < 0) {
				this._requests.splice(i, 1, request, this._requests[i]);
				return;
			}
		}
		this._requests.push(request);
	},
	peek: function () {
		return (this.isEmpty() ? false : this._requests[0]);
	},
	requestComplete: function (request) {
		for (i = 0; i < this._removedLen; i++) {
			if (this._removed[i].item == request) {
				this._removed.splice(i, 1);
				--this._removedLen;
				out('removed from _removed');
				return true;
			}
		}
		return false;
	},
	processRequest: function() {
		if (!this._intervalId) {
			this._runInterval();
			this._start();
		}
		this._idle = 0;
	},
	_runInterval: function() {
		if (Math.random() < this.checkRetryChance) {
			this._doRetries();
		}
		if (this.isEmpty()) {
			if (++this._idle > this.idleMax) {
				this._stop();
			}
			return;
		}
		var client = HTML_AJAX.httpClient();
		if (!client) {
			return;
		}
		var request = this.peek();
		if (!request) {
			this._requests.splice(0, 1);
			return;
		}
		client.request = request.item;
		client.makeRequest();
		this._requests.splice(0, 1);
		--this._len;
		this._removed[this._removedLen++] = new HTML_AJAX_Queue_Priority_Item(request, new Date().getTime());
	},
	_doRetries: function () {
		for (i = 0; i < this._removedLen; i++) {
			if (this._removed[i].time + this._requestTimeout < new Date().getTime()) {
				this.addRequest(request.item);
				this._removed.splice(i, 1);
				--this._removedLen;
				return true;
			}
		}
	},
	_start: function() {
		var self = this;
		this._intervalId = setInterval(function() { self._runInterval() }, this.interval);
	},
	_stop: function() {
		clearInterval(this._intervalId);
		this._intervalId = 0;
	}
};
// clientPool.js
HTML_AJAX_Client_Pool = function(maxClients, startingClients)
{
	this.maxClients = maxClients;
	this._clients = [];
	this._len = 0;
	while (--startingClients > 0) {
		this.addClient();
	}
}

HTML_AJAX_Client_Pool.prototype = {
	isEmpty: function()
	{
		return this._len == 0;
	},
	addClient: function()
	{
		if (this.maxClients != 0 && this._len > this.maxClients) {
			return false;
		}
		var key = this._len++;
		this._clients[key] = new HTML_AJAX_HttpClient();
		return this._clients[key];
	},
	getClient: function ()
	{
		for (var i = 0; i < this._len; i++) {
			if (!this._clients[i].callInProgress() && this._clients[i].callbackComplete) {
				return this._clients[i];
			}
		}
		var client = this.addClient();
		if (client) {
			return client;
		}
		return false;
	},
	removeClient: function (client)
	{
		for (var i = 0; i < this._len; i++) {
			if (!this._clients[i] == client) {
				this._clients.splice(i, 1);
				return true;
			}
		}
		return false;
	},
	clear: function ()
	{
		this._clients = [];
		this._len = 0;
	}
};

// create a default client pool with unlimited clients
HTML_AJAX.clientPools['default'] = new HTML_AJAX_Client_Pool(0);
// IframeXHR.js
/**
 * XMLHttpRequest Iframe fallback
 *
 * http://lxr.mozilla.org/seamonkey/source/extensions/xmlextras/tests/ - should work with these
 *
 * @category	HTML
 * @package	AJAX
 * @author	Elizabeth Smith <auroraeosrose@gmail.com>
 * @copyright 	2005 Elizabeth Smith
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */
HTML_AJAX_IframeXHR_instances = new Object();
function HTML_AJAX_IframeXHR()
{
	this._id = 'HAXHR_iframe_' + new Date().getTime();
	HTML_AJAX_IframeXHR_instances[this._id] = this;
}
HTML_AJAX_IframeXHR.prototype = {
// Data not sent with text/xml Content-Type will only be available via the responseText property

	// properties available in safari/mozilla/IE xmlhttprequest object
	onreadystatechange: null, // Event handler for an event that fires at every state change
	readyState: 0, // Object status integer: 0 = uninitialized 1 = loading 2 = loaded 3 = interactive 4 = complete
	responseText: '', // String version of data returned from server process
	responseXML: null, // DOM-compatible document object of data returned from server process
	status: 0, // Numeric code returned by server, such as 404 for "Not Found" or 200 for "OK"
	statusText: '', // String message accompanying the status code
	iframe: true, // flag for iframe

	//these are private properties used internally to keep track of stuff
	_id: null, // iframe id, unique to object(hopefully)
	_url: null, // url sent by open
	_method: null, // get or post
	_async: null, // sync or async sent by open
	_headers: new Object(), //request headers to send, actually sent as form vars
	_response: new Object(), //response headers received
	_phpclass: null, //class to send
	_phpmethod: null, //method to send
	_history: null, // opera has to have history munging

	// Stops the current request
	abort: function()
	{
		var iframe = document.getElementById(this._id);
		if (iframe) {
			document.body.removeChild(iframe);
		}
		if (this._timeout) {
			window.clearTimeout(this._timeout);
		}
		this.readyState = 1;
		if (typeof(this.onreadystatechange) == "function") {
			this.onreadystatechange();
		}
	},

	// This will send all headers in this._response and will include lastModified and contentType if not already set
	getAllResponseHeaders: function()
	{
		var string = '';
		for (i in this._response) {
			string += i + ' : ' + this._response[i] + "\n";
		}
		return string;
	},

	// This will use lastModified and contentType if they're not set
	getResponseHeader: function(header)
	{
		return (this._response[header] ? this._response[header] : null);
	},

	// Assigns a label/value pair to the header to be sent with a request
	setRequestHeader: function(label, value) {
		this._headers[label] = value;
		return; },

	// Assigns destination URL, method, and other optional attributes of a pending request
	open: function(method, url, async, username, password)
	{
		if (!document.body) {
			throw('CANNOT_OPEN_SEND_IN_DOCUMENT_HEAD');
		}
		//exceptions for not enough arguments
		if (!method || !url) {
			throw('NOT_ENOUGH_ARGUMENTS:METHOD_URL_REQUIRED');
		}
		//get and post are only methods accepted
		this._method = (method.toUpperCase() == 'POST' ? 'POST' : 'GET');
		this._decodeUrl(url);
		this._async = async;
		if(!this._async && document.readyState && !window.opera) {
			throw('IE_DOES_NOT_SUPPORT_SYNC_WITH_IFRAMEXHR');
		}
		//set status to loading and call onreadystatechange
		this.readyState = 1;
		if(typeof(this.onreadystatechange) == "function") {
			this.onreadystatechange();
		}
	},

	// Transmits the request, optionally with postable string or DOM object data
	send: function(content)
	{
		//attempt opera history munging
		if (window.opera) {
			this._history = window.history.length;
		}
		//create a "form" for the contents of the iframe
		var form = '<html><body><form method="'
			+ (this._url.indexOf('px=') < 0 ? this._method : 'post')
			+ '" action="' + this._url + '">';
		//tell iframe unwrapper this IS an iframe
		form += '<input name="Iframe_XHR" value="1" />';
		//class and method
		if (this._phpclass != null) {
			form += '<input name="Iframe_XHR_class" value="' + this._phpclass + '" />';
		}
		if (this._phpmethod != null) {
			form += '<input name="Iframe_XHR_method" value="' + this._phpmethod + '" />';
		}
		// fake headers
		for (label in this._headers) {
			form += '<textarea name="Iframe_XHR_headers[]">' + label +':'+ this._headers[label] + '</textarea>';
		}
		// add id
		form += '<textarea name="Iframe_XHR_id">' + this._id + '</textarea>';
		if (content != null && content.length > 0) {
			form += '<textarea name="Iframe_XHR_data">' + content + '</textarea>';
		}
		form += '<input name="Iframe_XHR_HTTP_method" value="' + this._method + '" />';
		form += '<s'+'cript>document.forms[0].submit();</s'+'cript></form></body></html>';
		form = "javascript:document.write('" + form.replace(/\'/g,"\\'") + "');void(0);";
		this.readyState = 2;
		if (typeof(this.onreadystatechange) == "function") {
			this.onreadystatechange();
		}
		// try to create an iframe with createElement and append node
		try {
			var iframe = document.createElement('iframe');
			iframe.id = this._id;
			// display: none will fail on some browsers
			iframe.style.visibility = 'hidden';
			// for old browsers with crappy css
			iframe.style.border = '0';
			iframe.style.width = '0';
			iframe.style.height = '0';

			if (document.all) {
				// MSIE, opera
				iframe.src = form;
				document.body.appendChild(iframe);
			} else {
				document.body.appendChild(iframe);
				iframe.src = form;
			}
		} catch(exception) {
			// dom failed, write the sucker manually
			var html = '<iframe src="' + form +'" id="' + this._id + '" style="visibility:hidden;border:0;height:0;width:0;"></iframe>';
			document.body.innerHTML += html;
		}
		if (this._async == true) {
			//avoid race state if onload is called first
			if (this.readyState < 3) {
				this.readyState = 3;
				if(typeof(this.onreadystatechange) == "function") {
					this.onreadystatechange();
				}
			}
		} else {
			//we force a while loop for sync, it's ugly but hopefully it works
			while (this.readyState != 4) {
				//just check to see if we can up readyState
				if (this.readyState < 3) {
					this.readyState = 3;
					if(typeof(this.onreadystatechange) == "function") {
						this.onreadystatechange();
					}
				}
			}
		}
	},

	// attached as an onload function to the iframe to trigger when we're done
	isLoaded: function(headers, data)
	{
		this.readyState = 4;
		//set responseText, Status, StatusText
		this.status = 200;
		this.statusText = 'OK';
		this.responseText = data;
		this._response = headers;
		if (!this._response['Last-Modified']) {
			string += 'Last-Modified : ' + document.getElementById(this._id).lastModified + "\n";
		}
		if (!this._response['Content-Type']) {
			string += 'Content-Type : ' + document.getElementById(this._id).contentType + "\n";
		}
		// if this is xml populate responseXML accordingly
		if (this._response['Content-Type'] == 'application/xml')
		{
			return new DOMParser().parseFromString(this.responseText, 'application/xml');
		}
		//attempt opera history munging in opera 8+ - this is a REGRESSION IN OPERA
		if (window.opera && window.opera.version) {
			//go back current history - old history
			window.history.go(this._history - window.history.length);
		}
		if (typeof(this.onreadystatechange) == "function") {
			this.onreadystatechange();
		}
		document.body.removeChild(document.getElementById(this._id));
	},

	// strip off the c and m from the url send...yuck
	_decodeUrl: function(querystring)
	{
		//opera 7 is too stupid to do a relative url...go figure
		var url = unescape(location.href);
		url = url.substring(0, url.lastIndexOf("/") + 1);
		var item = querystring.split('?');
		//rip off any path info and append to path above <-  relative paths (../) WILL screw this
		this._url = url + item[0].substring(item[0].lastIndexOf("/") + 1,item[0].length);
		if(item[1]) {
			item = item[1].split('&');
			for (i in item) {
				var v = item[i].split('=');
				if (v[0] == 'c') {
					this._phpclass = v[1];
				} else if (v[0] == 'm') {
					this._phpmethod = v[1];
				}
			}
		}
		if (!this._phpclass || !this._phpmethod) {
			var cloc = window.location.href;
			this._url = cloc + (cloc.indexOf('?') >= 0 ? '&' : '?') + 'px=' + escape(HTML_AJAX_Util.absoluteURL(querystring));
		}
	}
}
// serializer/UrlSerializer.js
// {{{ HTML_AJAX_Serialize_Urlencoded
/**
 * URL-encoding serializer
 *
 * This class can be used to serialize and unserialize data in a
 * format compatible with PHP's handling of HTTP query strings.
 * Due to limitations of the format, all input is serialized as an
 * array or a string. See examples/serialize.url.examples.php
 *
 * @version	0.0.1
 * @copyright	2005 Arpad Ray <arpad@php.net>
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 *
 * See Main.js for Author/license details
 */
function HTML_AJAX_Serialize_Urlencoded() {}
HTML_AJAX_Serialize_Urlencoded.prototype = {
	contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
	base: '_HTML_AJAX',
	_keys: [],
	error: false,
	message: "",
	cont: "",
	// {{{ serialize
	/**
	 *  Serializes a variable
	 *
	 *  @param	mixed  inp the variable to serialize
	 *  @return	string   a string representation of the input,
	 *					  which can be reconstructed by unserialize()
	 */
	serialize: function(input, _internal) {
		if (typeof input == 'undefined') {
			return '';
		}
		if (!_internal) {
			this._keys = [];
		}
		var ret = '', first = true;
		for (i = 0; i < this._keys.length; i++) {
			ret += (first ? HTML_AJAX_Util.encodeUrl(this._keys[i]) : '[' + HTML_AJAX_Util.encodeUrl(this._keys[i]) + ']');
			first = false;
		}
		ret += '=';
		switch (HTML_AJAX_Util.getType(input)) {
			case 'string':
			case 'number':
				ret += HTML_AJAX_Util.encodeUrl(input.toString());
				break;
			case 'boolean':
				ret += (input ? '1' : '0');
				break;
			case 'array':
			case 'object':
				ret = '';
				for (i in input) {
					this._keys.push(i);
					ret += this.serialize(input[i], true) + '&';
					this._keys.pop();
				}
				ret = ret.substr(0, ret.length - 1);
		}
		return ret;
	},
	// }}}
	// {{{ unserialize
	/**
	 *  Reconstructs a serialized variable
	 *
	 *  @param	string inp the string to reconstruct
	 *  @return	array an array containing the variable represented by the input string, or void on failure
	 */
	unserialize: function(input) {
		if (!input.length || input.length == 0) {
			// null
			return;
		}
		if (!/^(\w+(\[[^\[\]]*\])*=[^&]*(&|$))+$/.test(input)) {
			this.raiseError("invalidly formed input", input);
			return;
		}
		input = input.split("&");
		var pos, key, keys, val, _HTML_AJAX = [];
		if (input.length == 1) {
			return HTML_AJAX_Util.decodeUrl(input[0].substr(this.base.length + 1));
		}
		for (var i in input) {
			pos = input[i].indexOf("=");
			if (pos < 1 || input[i].length - pos - 1 < 1) {
				this.raiseError("input is too short", input[i]);
				return;
			}
			key = HTML_AJAX_Util.decodeUrl(input[i].substr(0, pos));
			val = HTML_AJAX_Util.decodeUrl(input[i].substr(pos + 1));
			key = key.replace(/\[((\d*\D+)+)\]/g, '["$1"]');
			keys = key.split(']');
			for (j in keys) {
				if (!keys[j].length || keys[j].length == 0) {
					continue;
				}
				try {
					if (eval('typeof ' + keys[j] + ']') == 'undefined') {
						var ev = keys[j] + ']=[];';
						eval(ev);
					}
				} catch (e) {
					this.raiseError("error evaluating key", ev);
					return;
				}
			}
			try {
				eval(key + '="' + val + '";');
			} catch (e) {
				this.raiseError("error evaluating value", input);
				return;
			}
		}
		return _HTML_AJAX;
	},
	// }}}
	// {{{ getError
	/**
	*  Gets the last error message
	*
	*  @return	string   the last error message from unserialize()
	*/
	getError: function() {
		return this.message + "\n" + this.cont;
	},
	// }}}
	// {{{ raiseError
	/**
	*  Raises an eror (called by unserialize().)
	*
	*  @param	string	message	the error message
	*  @param	string	cont	   the remaining unserialized content
	*/
	raiseError: function(message, cont) {
		this.error = 1;
		this.message = message;
		this.cont = cont;
	}
	// }}}
}
// }}}
// serializer/phpSerializer.js
// {{{ HTML_AJAX_Serialize_PHP
/**
 * PHP serializer
 *
 * This class can be used to serialize and unserialize data in a
 * format compatible with PHP's native serialization functions.
 *
 * @version	0.0.3
 * @copyright	2005 Arpad Ray <arpad@php.net>
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 *
 * See Main.js for Author/license details
 */

function HTML_AJAX_Serialize_PHP() {}
HTML_AJAX_Serialize_PHP.prototype = {
	error: false,
	message: "",
	cont: "",
	defaultEncoding: 'UTF-8',
	contentType: 'application/php-serialized; charset: UTF-8',
	// {{{ serialize
	/**
	*  Serializes a variable
	*
	*  @param	mixed  inp the variable to serialize
	*  @return	string   a string representation of the input,
	*					  which can be reconstructed by unserialize()
	*  @author Arpad Ray <arpad@rajeczy.com>
	*  @author David Coallier <davidc@php.net>
	*/
	serialize: function(inp) {
		var type = HTML_AJAX_Util.getType(inp);
		var val;
		switch (type) {
			case "undefined":
				val = "N";
				break;
			case "boolean":
				val = "b:" + (inp ? "1" : "0");
				break;
			case "number":
				val = (Math.round(inp) == inp ? "i" : "d") + ":" + inp;
				break;
			case "string":
				val = "s:" + inp.length + ":\"" + inp + "\"";
				break;
			case "array":
				val = "a";
			case "object":
				if (type == "object") {
					var objname = inp.constructor.toString().match(/(\w+)\(\)/);
					if (objname == undefined) {
						return;
					}
					objname[1] = this.serialize(objname[1]);
					val = "O" + objname[1].substring(1, objname[1].length - 1);
				}
				var count = 0;
				var vals = "";
				var okey;
				for (key in inp) {
					okey = (key.match(/^[0-9]+$/) ? parseInt(key) : key);
					vals += this.serialize(okey) +
							this.serialize(inp[key]);
					count++;
				}
				val += ":" + count + ":{" + vals + "}";
				break;
		}
		if (type != "object" && type != "array") val += ";";
		return val;
	},
	// }}}
	// {{{ unserialize
	/**
	 *  Reconstructs a serialized variable
	 *
	 *  @param	string inp the string to reconstruct
	 *  @return   mixed the variable represented by the input string, or void on failure
	 */
	unserialize: function(inp) {
		this.error = 0;
		if (inp == "" || inp.length < 2) {
			this.raiseError("input is too short");
			return;
		}
		var val, kret, vret, cval;
		var type = inp.charAt(0);
		var cont = inp.substring(2);
		var size = 0, divpos = 0, endcont = 0, rest = "", next = "";

		switch (type) {
		case "N": // null
			if (inp.charAt(1) != ";") {
				this.raiseError("missing ; for null", cont);
			}
			// leave val undefined
			rest = cont;
			break;
		case "b": // boolean
			if (!/[01];/.test(cont.substring(0,2))) {
				this.raiseError("value not 0 or 1, or missing ; for boolean", cont);
			}
			val = (cont.charAt(0) == "1");
			rest = cont.substring(1);
			break;
		case "s": // string
			val = "";
			divpos = cont.indexOf(":");
			if (divpos == -1) {
				this.raiseError("missing : for string", cont);
				break;
			}
			size = parseInt(cont.substring(0, divpos));
			if (size == 0) {
				if (cont.length - divpos < 4) {
					this.raiseError("string is too short", cont);
					break;
				}
				rest = cont.substring(divpos + 4);
				break;
			}
			if ((cont.length - divpos - size) < 4) {
				this.raiseError("string is too short", cont);
				break;
			}
			if (cont.substring(divpos + 2 + size, divpos + 4 + size) != "\";") {
				this.raiseError("string is too long, or missing \";", cont);
			}
			val = cont.substring(divpos + 2, divpos + 2 + size);
			rest = cont.substring(divpos + 4 + size);
			break;
		case "i": // integer
		case "d": // float
			var dotfound = 0;
			for (var i = 0; i < cont.length; i++) {
				cval = cont.charAt(i);
				if (isNaN(parseInt(cval)) && !(type == "d" && cval == "." && !dotfound++)) {
					endcont = i;
					break;
				}
			}
			if (!endcont || cont.charAt(endcont) != ";") {
				this.raiseError("missing or invalid value, or missing ; for int/float", cont);
			}
			val = cont.substring(0, endcont);
			val = (type == "i" ? parseInt(val) : parseFloat(val));
			rest = cont.substring(endcont + 1);
			break;
		case "a": // array
			if (cont.length < 4) {
				this.raiseError("array is too short", cont);
				return;
			}
			divpos = cont.indexOf(":", 1);
			if (divpos == -1) {
				this.raiseError("missing : for array", cont);
				return;
			}
			size = parseInt(cont.substring(0, divpos));
			cont = cont.substring(divpos + 2);
			val = new Array();
			if (cont.length < 1) {
				this.raiseError("array is too short", cont);
				return;
			}
			for (var i = 0; i < size; i++) {
				kret = this.unserialize(cont, 1);
				if (this.error || kret[0] == undefined || kret[1] == "") {
					this.raiseError("missing or invalid key, or missing value for array", cont);
					return;
				}
				vret = this.unserialize(kret[1], 1);
				if (this.error) {
					this.raiseError("invalid value for array", cont);
					return;
				}
				val[kret[0]] = vret[0];
				cont = vret[1];
			}
			if (cont.charAt(0) != "}") {
				this.raiseError("missing ending }, or too many values for array", cont);
				return;
			}
			rest = cont.substring(1);
			break;
		case "O": // object
			divpos = cont.indexOf(":");
			if (divpos == -1) {
				this.raiseError("missing : for object", cont);
				return;
			}
			size = parseInt(cont.substring(0, divpos));
			var objname = cont.substring(divpos + 2, divpos + 2 + size);
			if (cont.substring(divpos + 2 + size, divpos + 4 + size) != "\":") {
				this.raiseError("object name is too long, or missing \":", cont);
				return;
			}
			var objprops = this.unserialize("a:" + cont.substring(divpos + 4 + size), 1);
			if (this.error) {
				this.raiseError("invalid object properties", cont);
				return;
			}
			rest = objprops[1];
			var objout = "function " + objname + "(){";
			for (key in objprops[0]) {
				objout += "this." + key + "=objprops[0]['" + key + "'];";
			}
			objout += "}val=new " + objname + "();";
			eval(objout);
			break;
		default:
			this.raiseError("invalid input type", cont);
		}
		return (arguments.length == 1 ? val : [val, rest]);
	},
	// }}}
	// {{{ getError
	/**
	*  Gets the last error message
	*
	*  @return	string   the last error message from unserialize()
	*/
	getError: function() {
		return this.message + "\n" + this.cont;
	},
	// }}}
	// {{{ raiseError
	/**
	*  Raises an eror (called by unserialize().)
	*
	*  @param	string	message	the error message
	*  @param	string	cont	   the remaining unserialized content
	*/
	raiseError: function(message, cont) {
		this.error = 1;
		this.message = message;
		this.cont = cont;
	}
	// }}}
}
// }}}

// Dispatcher.js
/**
 * Class that is used by generated stubs to make actual AJAX calls
 *
 * @category    HTML
 * @package 	AJAX
 * @author	Joshua Eichorn <josh@bluga.net>
 * @copyright   2005 Joshua Eichorn
 * @license 	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */
function HTML_AJAX_Dispatcher(className,mode,callback,serverUrl,serializerType)
{
	this.className = className;
	this.mode = mode;
	this.callback = callback;
	this.serializerType = serializerType;

	if (serverUrl) {
		this.serverUrl = serverUrl;
	}
	else {
		this.serverUrl = window.location;
	}
}

HTML_AJAX_Dispatcher.prototype = {
	/**
	 * Queue to use when making a request
	 */
	queue: 'default',

	/**
	 * Timeout for async calls
	 */
	timeout: 20000,

	/**
	 * Default request priority
	 */
	priority: 0,

	/**
	 * Request options
	 */
	options: {},

	/**
	 * Make an ajax call
	 *
	 * @param   string callName
	 * @param   Array   args	arguments to the report method
	 */
	doCall: function(callName,args)
	{
		var request = new HTML_AJAX_Request();
		request.requestUrl = this.serverUrl;
		request.className = this.className;
		request.methodName = callName;
		request.timeout = this.timeout;
		request.contentType = this.contentType;
		request.serializer = eval('new HTML_AJAX_Serialize_'+this.serializerType);
		request.queue = this.queue;
		request.priority = this.priority;

		for(var i in this.options) {
			request[i] = this.options[i];
		}

		for(var i=0; i < args.length; i++) {
			request.addArg(i,args[i]);
		}

		if ( this.mode == "async" ) {
			request.isAsync = true;
			if (this.callback[callName]) {
				var self = this;
				request.callback = function(result) { self.callback[callName](result); }
			}

		} else {
			request.isAsync = false;
		}

		return HTML_AJAX.makeRequest(request);
	},

	Sync: function()
	{
		this.mode = 'sync';
	},

	Async: function(callback)
	{
		this.mode = 'async';
		if (callback) {
			this.callback = callback;
		}
	}
};
// HttpClient.js
/**
 * XMLHttpRequest Wrapper
 * @category    HTML
 * @package 	AJAX
 * @author      Joshua Eichorn <josh@bluga.net>
 * @copyright   2005 Joshua Eichorn
 * @license     http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */
function HTML_AJAX_HttpClient() { }
HTML_AJAX_HttpClient.prototype = {
	// request object
	request: null,

	// timeout id
	_timeoutId: null,

	callbackComplete: true,

	// has this request been aborted
	aborted: false,

	// method to initialize an xmlhttpclient
	init:function()
	{
		try {
			// Mozilla / Safari
			//this.xmlhttp = new HTML_AJAX_IframeXHR(); //uncomment these two lines to test iframe
			//return;
			this.xmlhttp = new XMLHttpRequest();
		} catch (e) {
			// IE
			var XMLHTTP_IDS = new Array(
			'MSXML2.XMLHTTP.5.0',
			'MSXML2.XMLHTTP.4.0',
			'MSXML2.XMLHTTP.3.0',
			'MSXML2.XMLHTTP',
			'Microsoft.XMLHTTP' );
			var success = false;
			for (var i=0;i < XMLHTTP_IDS.length && !success; i++) {
				try {
					this.xmlhttp = new ActiveXObject(XMLHTTP_IDS[i]);
					success = true;
				} catch (e) {}
			}
			if (!success) {
				try{
					this.xmlhttp = new HTML_AJAX_IframeXHR();
					this.request.iframe = true;
				} catch(e) {
					throw new Error('Unable to create XMLHttpRequest.');
				}
			}
		}
	},

	// check if there is a call in progress
	callInProgress: function()
	{
		switch ( this.xmlhttp.readyState ) {
			case 1:
			case 2:
			case 3:
				return true;
			break;
			default:
				return false;
			break;
		}
	},

	// make the request defined in the request object
	makeRequest: function()
	{
		if (!this.xmlhttp) {
			this.init();
		}

		try {
			if (this.request.Open) {
				this.request.Open();
			}
			else if (HTML_AJAX.Open) {
				HTML_AJAX.Open(this.request);
			}

			if (this.request.multipart) {
				if (document.all) {
					this.iframe = true;
				} else {
					this.xmlhttp.multipart = true;
				}
			}

			// set onreadystatechange here since it will be reset after a completed call in Mozilla
			var self = this;
			this.xmlhttp.open(this.request.requestType,this.request.completeUrl(),this.request.isAsync);
			if (this.request.customHeaders) {
				for (i in this.request.customHeaders) {
					this.xmlhttp.setRequestHeader(i, this.request.customHeaders[i]);
				}
			}
			if (this.request.customHeaders && !this.request.customHeaders['Content-Type']) {
				var content = this.request.getContentType();
				//opera is stupid for anything but plain text or xml!!
				if(window.opera && content != 'application/xml')
				{
					this.xmlhttp.setRequestHeader('Content-Type','text/plain; charset=utf-8');
					this.xmlhttp.setRequestHeader('x-Content-Type', content + '; charset=utf-8');
				}
				else
				{
					this.xmlhttp.setRequestHeader('Content-Type', content +  '; charset=utf-8');
				}
			}

			if (this.request.isAsync) {
				if (this.request.callback) {
					this.callbackComplete = false;
				}
				this.xmlhttp.onreadystatechange = function() { self._readyStateChangeCallback(); }
			} else {
				this.xmlhttp.onreadystatechange = function() {}
			}
			var payload = this.request.getSerializedPayload();
			if (payload) {
				this.xmlhttp.setRequestHeader('Content-Length', payload.length);
			}
			this.xmlhttp.send(payload);

			if (!this.request.isAsync) {
				if ( this.xmlhttp.status == 200 ) {
					HTML_AJAX.requestComplete(this.request);
					if (this.request.Load) {
						this.request.Load();
					} else if (HTML_AJAX.Load) {
						HTML_AJAX.Load(this.request);
					}

					return this._decodeResponse();
				} else {
					var e = new Error('['+this.xmlhttp.status +'] '+this.xmlhttp.statusText);
					e.headers = this.xmlhttp.getAllResponseHeaders();
					this._handleError(e);
				}
			}
			else {
				// setup timeout
				var self = this;
				this._timeoutId = window.setTimeout(function() { self.abort(true); },this.request.timeout);
			}
		} catch (e) {
			this._handleError(e);
		}
	},

	// abort an inprogress request
	abort: function (automatic)
	{
		if (this.callInProgress()) {
			this.aborted = true;
			this.xmlhttp.abort();

			if (automatic) {
				HTML_AJAX.requestComplete(this.request);
				this._handleError(new Error('Request Timed Out: time out was '+this.request.timeout+'ms'));
			}
		}
	},

	// internal method used to handle ready state changes
	_readyStateChangeCallback:function()
	{
		try {
			switch(this.xmlhttp.readyState) {
				// XMLHTTPRequest.open() has just been called
				case 1:
					break;
				// XMLHTTPRequest.send() has just been called
				case 2:
					if (this.request.Send) {
						this.request.Send();
					} else if (HTML_AJAX.Send) {
						HTML_AJAX.Send(this.request);
					}
					break;
				// Fetching response from server in progress
				case 3:
					if (this.request.Progress) {
						this.request.Progress();
					} else if (HTML_AJAX.Progress ) {
						HTML_AJAX.Progress(this.request);
					}
				break;
				// Download complete
				case 4:
					window.clearTimeout(this._timeoutId);
					if (this.aborted) {
						if (this.request.Load) {
							this.request.Load();
						} else if (HTML_AJAX.Load) {
							HTML_AJAX.Load(this.request);
						}
					}
					else if (this.xmlhttp.status == 200) {
						if (this.request.Load) {
							this.request.Load();
						} else if (HTML_AJAX.Load ) {
							HTML_AJAX.Load(this.request);
						}

						var response = this._decodeResponse();

						if (this.request.callback) {
							this.request.callback(response);
							this.callbackComplete = true;
						}
					}
					else {
						var e = new Error('HTTP Error Making Request: ['+this.xmlhttp.status+'] '+this.xmlhttp.statusText);
						this._handleError(e);
					}
					HTML_AJAX.requestComplete(this.request);
				break;
			}
		} catch (e) {
				this._handleError(e);
		}
	},

	// decode response as needed
	_decodeResponse: function() {
		//try for x-Content-Type first
		var content = null;
		try {
			content = this.xmlhttp.getResponseHeader('X-Content-Type');
		} catch(e) {}
		if(!content || content == null)
		{
			content = this.xmlhttp.getResponseHeader('Content-Type');
		}
		//strip anything after ;
		if(content.indexOf(';') != -1)
		{
			content = content.substring(0, content.indexOf(';'));
		}
		// hook for xml, it doesn't need to be unserialized
		if(content == 'application/xml')
		{
			return this.xmlhttp.responseXML;
		}
		var unserializer = HTML_AJAX.serializerForEncoding(content);
		//alert(this.xmlhttp.getAllResponseHeaders()); // some sort of debug hook is needed here
		return unserializer.unserialize(this.xmlhttp.responseText);
	},

	// handle sending an error where it needs to go
	_handleError: function(e)
	{
		HTML_AJAX.requestComplete(this.request,e);
		if (this.request.onError) {
			this.request.onError(e);
		} else if (HTML_AJAX.onError) {
			HTML_AJAX.onError(e,this.request);
		}
		else {
			throw e;
		}
	}
}
// Request.js
/**
 * Class that contains everything needed to make a request
 * This includes:
 *	The url were calling
 *	If were calling a remote method, the class and method name
 *	The payload, unserialized
 *	The timeout for async calls
 *	The callback method
 *	Optional event handlers: onError, Load, Send
 *	A serializer instance
 *
 * @category	HTML
 * @package	AJAX
 * @author	Joshua Eichorn <josh@bluga.net>
 * @copyright	2005 Joshua Eichorn
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 *
 * See Main.js for author/license details
 */
function HTML_AJAX_Request(serializer) {
	this.serializer = serializer;
}
HTML_AJAX_Request.prototype = {

	// Instance of a serializer
	serializer: null,

	// Is this an async request
	isAsync: false,

	// HTTP verb
	requestType: 'POST',

	// The actual URL the request is sent to
	requestUrl: '',

	// Remote Class
	className: null,

	// Remote Method
	methodName: null,

	// Timeout in milliseconds for requests
	timeout: 20000,

	// unserialized data, for rpc calls use add args, to send raw data just set this directly
	args: null,

	// async callback method
	callback: null,

	// Queue to push this request too
	queue: 'default',

	// default priority
	priority: 0,

	// a hash of headers to add to add to this request
	customHeaders: {'X-Requested-With': 'XMLHttpRequest', 'X-Ajax-Engine': 'HTML_AJAX/0.5.2'},

	// true if this request will be sent using iframes
	iframe: false,

	// is this a grab request? if so we need to proxy for iframes
	grab: false,

	// true if this request should expect a multipart response
	multipart: false,

	// remote callback
	phpCallback: false,

	/**
	 * Add an argument for the remote method
	 * @param string argument name
	 * @param mixed value
	 * @return void
	 * @throws Error code 1004
	 */
	addArg: function(name, value)
	{
		if ( !this.args ) {
			this.args = [];
		}
		if (!/[^a-zA-Z_0-9]/.test(name) ) {
			this.args[name] = value;
		} else {
			throw new Error('Invalid parameter name ('+name+')');
		}
	},

	/**
	 * Get the payload in a serialized manner
	 */
	getSerializedPayload: function() {
		return this.serializer.serialize(this.args);
	},

	/**
	 * Get the content type
	 */
	getContentType: function() {
		return this.serializer.contentType;
	},

	/**
	 * Get the complete url, adding in any needed get params for rpc
	 */
	completeUrl: function() {
		if (this.className || this.methodName) {
			this.addGet('c', this.className);
			this.addGet('m', this.methodName);
		}
		if (this.phpCallback) {
			if (HTML_AJAX_Util.getType(this.phpCallback) == 'array') {
				this.phpCallback = this.phpCallback.join('.');
			}
			this.addGet('cb', this.phpCallback);
		}
		if (this.multipart) {
			this.addGet('multipart', '1');
		}
		return this.requestUrl;
	},

	/**
	 * Compare to another request by priority
	 */
	compareTo: function(other) {
		if (this.priority == other.priority) {
			return 0;
		}
		return (this.priority > other.priority ? 1 : -1);
	},

	/**
	 * Add a GET argument
	 */
	addGet: function(name, value) {
		var url = new String(this.requestUrl);
		url += (url.indexOf('?') < 0 ? '?' : '&') + escape(name) + '=' + escape(value);
		this.requestUrl = url;
	}
}
// serializer/JSON.js
/*
Copyright (c) 2005 JSON.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
    The global object JSON contains two methods.

    JSON.stringify(value) takes a JavaScript value and produces a JSON text.
    The value must not be cyclical.

    JSON.parse(text) takes a JSON text and produces a JavaScript value. It will
    throw a 'JSONError' exception if there is an error.
*/
var HTML_AJAX_JSON = {
    copyright: '(c)2005 JSON.org',
    license: 'http://www.crockford.com/JSON/license.html',
/*
    Stringify a JavaScript value, producing a JSON text.
*/
    stringify: function (v) {
        var a = [];

/*
    Emit a string.
*/
        function e(s) {
            a[a.length] = s;
        }

/*
    Convert a value.
*/
        function g(x) {
            var c, i, l, v;

            switch (typeof x) {
            case 'object':
                if (x) {
                    if (x instanceof Array) {
                        e('[');
                        l = a.length;
                        for (i = 0; i < x.length; i += 1) {
                            v = x[i];
                            if (typeof v != 'undefined' &&
                                    typeof v != 'function') {
                                if (l < a.length) {
                                    e(',');
                                }
                                g(v);
                            }
                        }
                        e(']');
                        return;
                    } else if (typeof x.valueOf == 'function') {
                        e('{');
                        l = a.length;
                        for (i in x) {
                            v = x[i];
                            if (typeof v != 'undefined' &&
                                    typeof v != 'function' &&
                                    (!v || typeof v != 'object' ||
                                        typeof v.valueOf == 'function')) {
                                if (l < a.length) {
                                    e(',');
                                }
                                g(i);
                                e(':');
                                g(v);
                            }
                        }
                        return e('}');
                    }
                }
                e('null');
                return;
            case 'number':
                e(isFinite(x) ? +x : 'null');
                return;
            case 'string':
                l = x.length;
                e('"');
                for (i = 0; i < l; i += 1) {
                    c = x.charAt(i);
                    if (c >= ' ') {
                        if (c == '\\' || c == '"') {
                            e('\\');
                        }
                        e(c);
                    } else {
                        switch (c) {
                        case '\b':
                            e('\\b');
                            break;
                        case '\f':
                            e('\\f');
                            break;
                        case '\n':
                            e('\\n');
                            break;
                        case '\r':
                            e('\\r');
                            break;
                        case '\t':
                            e('\\t');
                            break;
                        default:
                            c = c.charCodeAt();
                            e('\\u00' + Math.floor(c / 16).toString(16) +
                                (c % 16).toString(16));
                        }
                    }
                }
                e('"');
                return;
            case 'boolean':
                e(String(x));
                return;
            default:
                e('null');
                return;
            }
        }
        g(v);
        return a.join('');
    },
/*
    Parse a JSON text, producing a JavaScript value.
*/
    parse: function (text) {
        return
(/^(\s+|[,:{}\[\]]|"(\\["\\\/bfnrtu]|[^\x00-\x1f"\\]+)*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)+$/.test(text))
 &&
            eval('(' + text + ')');
    }
};// serializer/haSerializer.js
/**
 * HTML_AJAX_Serialize_HA  - custom serialization
 *
 * This class is used with the JSON serializer and the HTML_AJAX_Action php class
 * to allow users to easily write data handling and dom manipulation related to
 * ajax actions directly from their php code
 *
 * See Main.js for Author/license details
 */
function HTML_AJAX_Serialize_HA() { }
HTML_AJAX_Serialize_HA.prototype =
{
	/**
	 *  Takes data from JSON - which should be parseable into a nice array
	 *  reads the action to take and pipes it to the right method
	 *
	 *  @param   string payload incoming data from php
	 *  @return   true on success, false on failure
	 */
	unserialize: function(payload)
	{
		var actions = eval(payload);
		for(var i = 0; i < actions.length; i++)
		{
			var action = actions[i];
			switch(action.action)
			{
				case 'prepend':
					this._prependAttr(action.id, action.attributes);
					break;
				case 'append':
					this._appendAttr(action.id, action.attributes);
					break;
				case 'assign':
					this._assignAttr(action.id, action.attributes);
					break;
				case 'clear':
					this._clearAttr(action.id, action.attributes);
					break;
				case 'create':
					this._createNode(action.id, action.tag, action.attributes, action.type);
					break;
				case 'replace':
					this._replaceNode(action.id, action.tag, action.attributes);
					break;
				case 'remove':
					this._removeNode(action.id);
					break;
				case 'script':
					this._insertScript(action.data);
					break;
				case 'alert':
					this._insertAlert(action.data);
					break;
			}
		}
	},
	/* Dispatch Methods */
	_prependAttr: function(id, attributes)
	{
		var node = document.getElementById(id);
		this._setAttrs(node, attributes, 'prepend');
	},
	_appendAttr: function(id, attributes)
	{
		var node = document.getElementById(id);
		this._setAttrs(node, attributes, 'append');
	},
	_assignAttr: function(id, attributes)
	{
		var node = document.getElementById(id);
		this._setAttrs(node, attributes);
	},
	_clearAttr: function(id, attributes)
	{
		var node = document.getElementById(id);
		for(var i = 0; i < attributes.length; i++)
		{
			if(attributes[i] == 'innerHTML')
			{
				HTML_AJAX_Util.setInnerHTML(node, '');
			}
			// value can't be removed
			else if(attributes[i] == 'value')
			{
				node.value = '';
			}
			// I'd use hasAttribute first but IE is stupid stupid stupid
			else
			{
				try
				{
					node.removeAttribute(attributes[i]);
				}
				catch(e)
				{
					node[i] = undefined;
				}
			}
		}
	},
	_createNode: function(id, tag, attributes, type)
	{
		var newnode = document.createElement(tag);
		this._setAttrs(newnode, attributes);
		switch(type)
		{
			case 'append':
				document.getElementById(id).appendChild(newnode);
				break
			case 'prepend':
				var parent = document.getElementById(id);
				var sibling = parent.firstChild;
				parent.insertBefore(newnode, sibling);
				break;
			case 'insertBefore':
				var sibling = document.getElementById(id);
				var parent = sibling.parentNode;
				parent.insertBefore(newnode, sibling);
				break;
			//this one is tricky, if it's the last one we use append child...ewww
			case 'insertAfter':
				var sibling = document.getElementById(id);
				var parent = sibling.parentNode;
				var next = sibling.nextSibling;
				if(next == null)
				{
					parent.appendChild(newnode);
				}
				else
				{
					parent.insertBefore(newnode, next);
				}
				break;
		}
	},
	_replaceNode: function(id, tag, attributes)
	{
		var node = document.getElementById(id);
		var parent = node.parentNode;
		var newnode = document.createElement(tag);
		this._setAttrs(newnode, attributes);
		parent.replaceChild(newnode, node);
	},
	_removeNode: function(id)
	{
		var node = document.getElementById(id);
		if(node)
		{
			var parent = node.parentNode;
			parent.removeChild(node);
		}
	},
	_insertScript: function(data)
	{
		eval(data);
	},
	_insertAlert: function(data)
	{
		alert(data);
	},
	/* Helper Methods */
	// should we move this to HTML_AJAX_Util???, just does the - case which we need for style
	_camelize: function(instr)
	{
		var p = instr.split('-');
		var out = p[0];
		for(var i = 1; i < p.length; i++) {
			out += p[i].charAt(0).toUpperCase()+p[i].substring(1);
		}
		return out;
	},
	_setAttrs: function(node, attributes, type)
	{
		switch(type)
		{
			case 'prepend':
				for (var i in attributes)
				{
					// innerHTML is extremely flakey - use util method for it
					if(i == 'innerHTML')
					{
						HTML_AJAX_Util.setInnerHTML(node, attributes[i], 'append');
					}
					//IE doesn't support setAttribute on style so we need to break it out and set each property individually
					else if(i == 'style')
					{
						var styles = [];
						if(attributes[i].indexOf(';'))
						{
							styles = attributes[i].split(';');
						}
						else
						{
							styles.push(attributes[i]);
						}
						for(var i = 0; i < styles.length; i++)
						{
							var r = styles[i].match(/^\s*(.+)\s*:\s*(.+)\s*$/);
							if(r)
							{
								node.style[this._camelize(r[1])] = r[2] + node.style[this._camelize(r[1])];
							}
						}
					}
					else
					{
						try
						{
							node[i] = attributes[i] + node[i];
						}
						catch(e){}
						node.setAttribute(i, attributes[i] + node[i]);
					}
				}
				break;
			case 'append':
			{
				for (var i in attributes)
				{
					// innerHTML is extremely flakey - use util method for it
					if(i == 'innerHTML')
					{
						HTML_AJAX_Util.setInnerHTML(node, attributes[i], 'append');
					}
					//IE doesn't support setAttribute on style so we need to break it out and set each property individually
					else if(i == 'style')
					{
						var styles = [];
						if(attributes[i].indexOf(';'))
						{
							styles = attributes[i].split(';');
						}
						else
						{
							styles.push(attributes[i]);
						}
						for(var i = 0; i < styles.length; i++)
						{
							var r = styles[i].match(/^\s*(.+)\s*:\s*(.+)\s*$/);
							if(r)
							{
								node.style[this._camelize(r[1])] = node.style[this._camelize(r[1])] + r[2];
							}
						}
					}
					else
					{
						try
						{
							node[i] = node[i] + attributes[i];
						}
						catch(e){}
						node.setAttribute(i, node[i] + attributes[i]);
					}
				}
				break;
			}
			default:
			{
				for (var i in attributes)
				{
					//innerHTML hack bailout
					if(i == 'innerHTML')
					{
						HTML_AJAX_Util.setInnerHTML(node, attributes[i]);
					}
					else if(i == 'style')
					{
						var styles = [];
						if(attributes[i].indexOf(';'))
						{
							styles = attributes[i].split(';');
						}
						else
						{
							styles.push(attributes[i]);
						}
						for(var i = 0; i < styles.length; i++)
						{
							var r = styles[i].match(/^\s*(.+)\s*:\s*(.+)\s*$/);
							if(r)
							{
								node.style[this._camelize(r[1])] = r[2];
							}
						}
					}
					else
					{
						try
						{
							node[i] = attributes[i];
						}
						catch(e){}
						node.setAttribute(i, attributes[i]);
					}
				}
			}
		}
	}
}
// Loading.js
/**
 * Default loading implementation
 *
 * @category	HTML
 * @package	Ajax
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 * @copyright	2005 Joshua Eichorn
 * see Main.js for license Author details
 */
HTML_AJAX.Open = function(request) {
	var loading = document.getElementById('HTML_AJAX_LOADING');
	if (!loading) {
		loading = document.createElement('div');
		loading.id = 'HTML_AJAX_LOADING';
		loading.innerHTML = 'Loading...';

		loading.style.color	 = '#fff';
		loading.style.position  = 'absolute';
		loading.style.top   = 0;
		loading.style.right	 = 0;
		loading.style.backgroundColor = '#f00';
		loading.style.border		= '1px solid #f99';
		loading.style.width		 = '80px';
		loading.style.padding	   = '4px';
		loading.style.fontFamily	= 'Arial, Helvetica, sans';
		loading.count = 0;

		document.body.insertBefore(loading,document.body.firstChild);
	}
	else {
		if (loading.count == undefined) {
			loading.count = 0;
		}
	}
	loading.count++;
	if (request.isAsync) {
		request.loadingId = window.setTimeout(function() { loading.style.display = 'block'; },500);
	}
	else {
		loading.style.display = 'block';
	}
}
HTML_AJAX.Load = function(request) {
	if (request.loadingId) {
		window.clearTimeout(request.loadingId);
	}
	var loading = document.getElementById('HTML_AJAX_LOADING');
	loading.count--;

	if (loading.count == 0) {
		loading.style.display = 'none';
	}
}
// util.js
/**
 * Utility methods
 *
 * @category	HTML
 * @package	Ajax
 * @license	http://www.opensource.org/licenses/lgpl-license.php  LGPL
 *
 * See Main.js for author/license details
 */
// {{{ HTML_AJAX_Util
/**
 * All the utilities we will be using thorough the classes
 */
var HTML_AJAX_Util = {
	// Set the element event
	registerEvent: function(element, event, handler)
	{
		element = this.getElement(element);
		if (typeof element.addEventListener != "undefined") {   //Dom2
			element.addEventListener(event, handler, false);
		} else if (typeof element.attachEvent != "undefined") { //IE 5+
			element.attachEvent("on" + event, handler);
		} else {
			if (element["on" + event] != null) {
				var oldHandler = element["on" + event];
				element["on" + event] = function(e) {
					oldHander(e);
					handler(e);
				};
			} else {
				element["on" + event] = handler;
			}
		}
	},
	// get the target of an event, automatically checks window.event for ie
	eventTarget: function(event)
	{
		if (!event) var event = window.event;
		if (event.target) return event.target; // w3c
		if (event.srcElement) return event.srcElement; // ie 5
	},
	// gets the type of a variable or its primitive equivalent as a string
	getType: function(inp)
	{
		var type = typeof inp, match;
		if(type == 'object' && !inp)
		{
			return 'null';
		}
		if (type == "object") {
			if(!inp.constructor)
			{
				return 'object';
			}
			var cons = inp.constructor.toString();
			if (match = cons.match(/(\w+)\(/)) {
				cons = match[1].toLowerCase();
			}
			var types = ["boolean", "number", "string", "array"];
			for (key in types) {
				if (cons == types[key]) {
					type = types[key];
					break;
				}
			}
		}
		return type;
	},
	// repeats the input string the number of times given by multiplier. exactly like PHP's str_repeat()
	strRepeat: function(inp, multiplier) {
		var ret = "";
		while (--multiplier > 0) ret += inp;
		return ret;
	},
	// encode a string allowing it to be used in a query string of a url
	encodeUrl: function(input) {
		return encodeURIComponent(input);
	},
	// decode a url encoded string
	decodeUrl: function(input) {
		return decodeURIComponent(input);
	},
	// recursive variable dumper similar in output to PHP's var_dump(), the differences being: this function displays JS types and type names; JS doesn't provide an object number like PHP does
	varDump: function(inp, printFuncs, _indent, _recursionLevel)
	{
		if (!_recursionLevel) _recursionLevel = 0;
		if (!_indent) _indent = 1;
		var tab = this.strRepeat("  ", ++_indent);
		var type = this.getType(inp), out = type;
		var consrx = /(\w+)\(/;
		consrx.compile();
		if (++_recursionLevel > 6) {
			return tab + inp + "Loop Detected\n";
		}
		switch (type) {
			case "boolean":
			case "number":
				out += "(" + inp.toString() + ")";
				break;
			case "string":
				out += "(" + inp.length + ") \"" + inp + "\"";
				break;
			case "function":
				if (printFuncs) {
					out += inp.toString().replace(/\n/g, "\n" + tab);
				}
				break;
			case "array":
			case "object":
				var atts = "", attc = 0;
				try {
					for (k in inp) {
						atts += tab + "[" + (/\D/.test(k) ? "\"" + k + "\"" : k)
							+ "]=>\n" + tab + this.varDump(inp[k], printFuncs, _indent, _recursionLevel);
						++attc;
					}
				} catch (e) {}
				if (type == "object") {
					var objname, objstr = inp.toString();
					if (objname = objstr.match(/^\[object (\w+)\]$/)) {
						objname = objname[1];
					} else {
						try {
							objname = inp.constructor.toString().match(consrx)[1];
						} catch (e) {
							objname = 'unknown';
						}
					}
					out += "(" + objname + ") ";
				}
				out += "(" + attc + ") {\n" + atts + this.strRepeat("  ", _indent - 1) +"}";
				break;
		}
		return out + "\n";
	},
	// non resursive simple debug printer
	quickPrint: function(input,sep) {
		if (!sep) {
			var sep = "\n";
		}
		var type = HTML_AJAX_Util.getType(input);
		switch (type) {
			case 'string':
				return input;
			case 'array':
				var ret = "";
				for(var i = 0; i < input.length; i++) {
					ret += i+':'+input[i]+sep;
				}
				return ret;
			default:
				var ret = "";
				for(var i in input) {
					ret += i+':'+input[i]+sep;
				}
				return ret;
		}
	},
	//compat function for stupid browsers in which getElementsByTag with a * dunna work
	getAllElements: function(parentElement)
	{
		//check for idiot browsers
		if( document.all)
		{
			if(!parentElement) {
				var allElements = document.all;
			}
			else
			{
				var allElements = [], rightName = new RegExp( parentElement, 'i' ), i;
				for( i=0; i<document.all.length; i++ ) {
					if( rightName.test( document.all[i].parentElement ) )
					allElements.push( document.all[i] );
				}
			}
			return allElements;
		}
		//real browsers just do this
		else
		{
			if (!parentElement) { parentElement = document.body; }
			return parentElement.getElementsByTagName('*');
		}
	},
	getElementsByProperty: function(property, regex, parentElement) {
		var allElements = HTML_AJAX_Util.getAllElements(parentElement);
		var items = [];
		for(var i=0,j=allElements.length; i<j; i++)
		{
			if(regex.test(allElements[i][property]))
			{
				items.push(allElements[i]);
			}
		}
		return items;
	},
	getElementsByClassName: function(className, parentElement) {
		return HTML_AJAX_Util.getElementsByProperty('className',new RegExp('(^| )' + className + '( |$)'),parentElement);
	},
	getElementsById: function(id, parentElement) {
		return HTML_AJAX_Util.getElementsByProperty('id',new RegExp(id),parentElement);
	},
	getElementsByCssSelector: function(selector,parentElement) {
		return cssQuery(selector,parentElement);
	},
	htmlEscape: function(inp) {
		var div = document.createElement('div');
		var text = document.createTextNode(inp);
		div.appendChild(text);
		return div.innerHTML;
	},
	// return the base of the given absolute url, or the filename if the second argument is true
	baseURL: function(absolute, filename) {
		var qPos = absolute.indexOf('?');
		if (qPos >= 0) {
			absolute = absolute.substr(0, qPos);
		}
		var slashPos = Math.max(absolute.lastIndexOf('/'), absolute.lastIndexOf('\\'));
		if (slashPos < 0) {
			return absolute;
		}
		return (filename ? absolute.substr(slashPos + 1) : absolute.substr(0, slashPos + 1));
	},
	// return the query string from a url
	queryString: function(url) {
		var qPos = url.indexOf('?');
		if (qPos >= 0) {
			return url.substr(qPos+1);
		}
	},
	// return the absolute path to the given relative url
	absoluteURL: function(rel, absolute) {
		if (/^https?:\/\//i.test(rel)) {
			return rel;
		}
		if (!absolute) {
			var bases = document.getElementsByTagName('base');
			for (i in bases) {
				if (bases[i].href) {
					absolute = bases[i].href;
					break;
				}
			}
			if (!absolute) {
				absolute = window.location.href;
			}
		}
		if (rel == '') {
			return absolute;
		}
		if (rel.substr(0, 2) == '//') {
			// starts with '//', replace everything but the protocol
			var slashesPos = absolute.indexOf('//');
			if (slashesPos < 0) {
				return 'http:' + rel;
			}
			return absolute.substr(0, slashesPos) + rel;
		}
		var base = this.baseURL(absolute);
		var absParts = base.substr(0, base.length - 1).split('/');
		var absHost = absParts.slice(0, 3).join('/') + '/';
		if (rel.substr(0, 1) == '/') {
			// starts with '/', append it to the host
			return absHost + rel;
		}
		if (rel.substr(0, 1) == '.' && rel.substr(1, 1) != '.') {
			// starts with '.', append it to the base
			return base + rel.substr(1);
		}
		// remove everything upto the path and beyond
		absParts.splice(0, 3);
		var relParts = rel.split('/');
		var loopStart = relParts.length - 1;
		relParts = absParts.concat(relParts);
		for (i = loopStart; i < relParts.length;) {
			if (relParts[i] == '..') {
				if (i == 0) {
					return absolute;
				}
				relParts.splice(i - 1, 2);
				--i;
				continue;
			}
			i++;
		}
		return absHost + relParts.join('/');
	},
	// sets the innerHTML of an element. the third param decides how to write, it replaces by default, others are append|prepend
	setInnerHTML: function(node, innerHTML, type)
	{
		node = this.getElement(node);

		if (type != 'append') {
			if (type == 'prepend') {
				var oldHtml = node.innerHTML;
			}
			node.innerHTML = '';
		}
		var good_browser = (navigator.product == 'Gecko');
		var regex = /^([\s\S]*?)<script([\s\S]*?)>([\s\S]*?)<\/script>([\s\S]*)$/i;
		var regex_src = /src=["'](.*?)["']/i;
		var matches, id, script, output = '', subject = innerHTML;
		var scripts = [];

		while (true) {
			matches = regex.exec(subject);
			if (matches && matches[0]) {
				subject = matches[4];
				id = 'ih_' + Math.round(Math.random()*9999) + '_' + Math.round(Math.random()*9999);

				var startLen = matches[3].length;
				script = matches[3].replace(/document\.write\(([\s\S]*?)\)/ig,
					'document.getElementById("' + id + '").innerHTML+=$1');

				output += matches[1];
				if (startLen != script.length) {
						output += '<span id="' + id + '"></span>';
				}

				output += '<script' + matches[2] + '>' + script + '</script>';
				if (good_browser) {
					continue;
				}
				if (script) {
					scripts.push(script);
				}
				if (regex_src.test(matches[2])) {
					var script_el = document.createElement("SCRIPT");
					var atts_regex = /(\w+)=["'](.*?)["']([\s\S]*)$/;
					var atts = matches[2];
					for (var i = 0; i < 5; i++) {
						var atts_matches = atts_regex.exec(atts);
						if (atts_matches && atts_matches[0]) {
							script_el.setAttribute(atts_matches[1], atts_matches[2]);
							atts = atts_matches[3];
						} else {
							break;
						}
					}
					scripts.push(script_el);
				}
			} else {
				output += subject;
				break;
			}
		}
		innerHTML = output;

		if (good_browser) {
			var el = document.createElement('span');
			el.innerHTML = innerHTML;

			for(var i = 0; i < el.childNodes.length; i++) {
				node.appendChild(el.childNodes[i].cloneNode(true));
			}
		}
		else {
			node.innerHTML += innerHTML;
		}

		if (oldHtml) {
			node.innerHTML += oldHtml;
		}

		if (!good_browser) {
			for(var i = 0; i < scripts.length; i++) {
				if (HTML_AJAX_Util.getType(scripts[i]) == 'string') {
					scripts[i] = scripts[i].replace(/^\s*<!(\[CDATA\[|--)|((\/\/)?--|\]\])>\s*$/g, '');
					window.eval(scripts[i]);
				}
				else {
					node.appendChild(scripts[i]);
				}
			}
		}
		return;
	},
	classSep: '(^|$| )',
	hasClass: function(o, className) {
		var o = this.getElement(o);
		var regex = new RegExp(this.classSep + className + this.classSep);
		return regex.test(o.className);
	},
	addClass: function(o, className) {
		var o = this.getElement(o);
		if(!this.hasClass(o, className)) {
			o.className += " " + className;
		}
	},
	removeClass: function(o, className) {
		var o = this.getElement(o);
		var regex = new RegExp(this.classSep + className + this.classSep);
		o.className = o.className.replace(regex, " ");
	},
	replaceClass: function(o, oldClass, newClass) {
		var o = this.getElement(o);
		var regex = new RegExp(this.classSep + oldClass + this.classSep);
		o.className = o.className.replace(regex, newClass);
	},
	getElement: function(el) {
		if (typeof el == 'string') {
			return document.getElementById(el);
		}
		return el;
	}
}
// }}}
// behavior/behavior.js
/**

ModifiedBehavior v1.0 by Ron Lancaster based on Ben Nolan's Behaviour, June 2005 implementation.
Modified to use Dean Edward's CSS Query.

Description
----------

Uses css selectors  to apply javascript Behaviors to enable unobtrusive javascript in html documents.

Dependencies
------------

Requires [Dean Edwards CSSQuery](http://dean.edwards.name/my/cssQuery/ "CSSQuery").

Usage
------

		Behavior.register(
			"b.someclass",
			function(element) {
				element.onclick = function(){
					alert(this.innerHTML);
				}
			}
		);

		Behavior.register(
			"#someid u",
			function(element) {
				element.onmouseover = function(){
					this.innerHTML = "BLAH!";
				}
			},
			getElementByID("parent")
		);

Call `Behavior.apply()` to re-apply the rules (if you update the dom, etc).

License
------

Reproduced under BSD licensed. Same license as Ben Nolan's implementation.

More information for Ben Nolan's implementation: <http://ripcord.co.nz/behaviour/>

*/

var Behavior = {
	// so to an id to get debug timings
	debug : false,

	// private data member
	list : new Array(),

	// private method
	addLoadEvent : function(func) {
		var oldonload = window.onload;

		if (typeof window.onload != 'function') {
			window.onload = func;
		} else {
			window.onload = function() {
				oldonload();
				func();
			}
		}
	},

	// void apply() : Applies the registered ruleset.
	apply : function() {
		if (this.debug) {
			document.getElementById(this.debug).innerHTML += 'Apply: '+new Date()+'<br>';
			var total = 0;
		}
		if (Behavior.list.length > 2) {
			cssQuery.caching = true;
		}
		for (i = 0; i < Behavior.list.length; i++) {
			var rule = Behavior.list[i];

			if (this.debug) { var ds = new Date() };
			var tags = cssQuery(rule.selector, rule.from);

			if (this.debug) {
				var de = new Date();
				var ts = de.valueOf()-ds.valueOf();
				document.getElementById(this.debug).innerHTML += 'Rule: '+rule.selector+' - Took: '+ts+' - Returned: '+tags.length+' tags<br>';
				total += ts;
			}
			if (tags) {
				for (j = 0; j < tags.length; j++) {
					rule.action(tags[j]);
				}
			}
		}
		if (Behavior.list.length > 2) {
			cssQuery.caching = false;
		}

		if (this.debug) {
			document.getElementById(this.debug).innerHTML += 'Total rule apply time: '+total;
		}
	},

	// void register() : register a css selector, and the action (function) to take,
	// from (optional) is a document, element or array of elements which is filtered by selector.
	register : function(selector, action, from) {
		Behavior.list.push(new BehaviorRule(selector, from, action));
	},

	// void start() : initial application of ruleset at document load.
	start : function() {
		Behavior.addLoadEvent(function() {
			Behavior.apply();
		});
	}
}

function BehaviorRule(selector, from, action) {
	this.selector = selector;
	this.from = from;
	this.action = action;
}

Behavior.start();
// behavior/cssQuery-p.js
/*
	cssQuery, version 2.0.2 (2005-08-19)
	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
	License: http://creativecommons.org/licenses/LGPL/2.1/
*/
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 x=6(){7 1D="2.0.2";7 C=/\\s*,\\s*/;7 x=6(s,A){33{7 m=[];7 u=1z.32.2c&&!A;7 b=(A)?(A.31==22)?A:[A]:[1g];7 1E=18(s).1l(C),i;9(i=0;i<1E.y;i++){s=1y(1E[i]);8(U&&s.Z(0,3).2b("")==" *#"){s=s.Z(2);A=24([],b,s[1])}1A A=b;7 j=0,t,f,a,c="";H(j<s.y){t=s[j++];f=s[j++];c+=t+f;a="";8(s[j]=="("){H(s[j++]!=")")a+=s[j];a=a.Z(0,-1);c+="("+a+")"}A=(u&&V[c])?V[c]:21(A,t,f,a);8(u)V[c]=A}m=m.30(A)}2a x.2d;5 m}2Z(e){x.2d=e;5[]}};x.1Z=6(){5"6 x() {\\n  [1D "+1D+"]\\n}"};7 V={};x.2c=L;x.2Y=6(s){8(s){s=1y(s).2b("");2a V[s]}1A V={}};7 29={};7 19=L;x.15=6(n,s){8(19)1i("s="+1U(s));29[n]=12 s()};x.2X=6(c){5 c?1i(c):o};7 D={};7 h={};7 q={P:/\\[([\\w-]+(\\|[\\w-]+)?)\\s*(\\W?=)?\\s*([^\\]]*)\\]/};7 T=[];D[" "]=6(r,f,t,n){7 e,i,j;9(i=0;i<f.y;i++){7 s=X(f[i],t,n);9(j=0;(e=s[j]);j++){8(M(e)&&14(e,n))r.z(e)}}};D["#"]=6(r,f,i){7 e,j;9(j=0;(e=f[j]);j++)8(e.B==i)r.z(e)};D["."]=6(r,f,c){c=12 1t("(^|\\\\s)"+c+"(\\\\s|$)");7 e,i;9(i=0;(e=f[i]);i++)8(c.l(e.1V))r.z(e)};D[":"]=6(r,f,p,a){7 t=h[p],e,i;8(t)9(i=0;(e=f[i]);i++)8(t(e,a))r.z(e)};h["2W"]=6(e){7 d=Q(e);8(d.1C)9(7 i=0;i<d.1C.y;i++){8(d.1C[i]==e)5 K}};h["2V"]=6(e){};7 M=6(e){5(e&&e.1c==1&&e.1f!="!")?e:23};7 16=6(e){H(e&&(e=e.2U)&&!M(e))28;5 e};7 G=6(e){H(e&&(e=e.2T)&&!M(e))28;5 e};7 1r=6(e){5 M(e.27)||G(e.27)};7 1P=6(e){5 M(e.26)||16(e.26)};7 1o=6(e){7 c=[];e=1r(e);H(e){c.z(e);e=G(e)}5 c};7 U=K;7 1h=6(e){7 d=Q(e);5(2S d.25=="2R")?/\\.1J$/i.l(d.2Q):2P(d.25=="2O 2N")};7 Q=6(e){5 e.2M||e.1g};7 X=6(e,t){5(t=="*"&&e.1B)?e.1B:e.X(t)};7 17=6(e,t,n){8(t=="*")5 M(e);8(!14(e,n))5 L;8(!1h(e))t=t.2L();5 e.1f==t};7 14=6(e,n){5!n||(n=="*")||(e.2K==n)};7 1e=6(e){5 e.1G};6 24(r,f,B){7 m,i,j;9(i=0;i<f.y;i++){8(m=f[i].1B.2J(B)){8(m.B==B)r.z(m);1A 8(m.y!=23){9(j=0;j<m.y;j++){8(m[j].B==B)r.z(m[j])}}}}5 r};8(![].z)22.2I.z=6(){9(7 i=0;i<1z.y;i++){o[o.y]=1z[i]}5 o.y};7 N=/\\|/;6 21(A,t,f,a){8(N.l(f)){f=f.1l(N);a=f[0];f=f[1]}7 r=[];8(D[t]){D[t](r,A,f,a)}5 r};7 S=/^[^\\s>+~]/;7 20=/[\\s#.:>+~()@]|[^\\s#.:>+~()@]+/g;6 1y(s){8(S.l(s))s=" "+s;5 s.P(20)||[]};7 W=/\\s*([\\s>+~(),]|^|$)\\s*/g;7 I=/([\\s>+~,]|[^(]\\+|^)([#.:@])/g;7 18=6(s){5 s.O(W,"$1").O(I,"$1*$2")};7 1u={1Z:6(){5"\'"},P:/^(\'[^\']*\')|("[^"]*")$/,l:6(s){5 o.P.l(s)},1S:6(s){5 o.l(s)?s:o+s+o},1Y:6(s){5 o.l(s)?s.Z(1,-1):s}};7 1s=6(t){5 1u.1Y(t)};7 E=/([\\/()[\\]?{}|*+-])/g;6 R(s){5 s.O(E,"\\\\$1")};x.15("1j-2H",6(){D[">"]=6(r,f,t,n){7 e,i,j;9(i=0;i<f.y;i++){7 s=1o(f[i]);9(j=0;(e=s[j]);j++)8(17(e,t,n))r.z(e)}};D["+"]=6(r,f,t,n){9(7 i=0;i<f.y;i++){7 e=G(f[i]);8(e&&17(e,t,n))r.z(e)}};D["@"]=6(r,f,a){7 t=T[a].l;7 e,i;9(i=0;(e=f[i]);i++)8(t(e))r.z(e)};h["2G-10"]=6(e){5!16(e)};h["1x"]=6(e,c){c=12 1t("^"+c,"i");H(e&&!e.13("1x"))e=e.1n;5 e&&c.l(e.13("1x"))};q.1X=/\\\\:/g;q.1w="@";q.J={};q.O=6(m,a,n,c,v){7 k=o.1w+m;8(!T[k]){a=o.1W(a,c||"",v||"");T[k]=a;T.z(a)}5 T[k].B};q.1Q=6(s){s=s.O(o.1X,"|");7 m;H(m=s.P(o.P)){7 r=o.O(m[0],m[1],m[2],m[3],m[4]);s=s.O(o.P,r)}5 s};q.1W=6(p,t,v){7 a={};a.B=o.1w+T.y;a.2F=p;t=o.J[t];t=t?t(o.13(p),1s(v)):L;a.l=12 2E("e","5 "+t);5 a};q.13=6(n){1d(n.2D()){F"B":5"e.B";F"2C":5"e.1V";F"9":5"e.2B";F"1T":8(U){5"1U((e.2A.P(/1T=\\\\1v?([^\\\\s\\\\1v]*)\\\\1v?/)||[])[1]||\'\')"}}5"e.13(\'"+n.O(N,":")+"\')"};q.J[""]=6(a){5 a};q.J["="]=6(a,v){5 a+"=="+1u.1S(v)};q.J["~="]=6(a,v){5"/(^| )"+R(v)+"( |$)/.l("+a+")"};q.J["|="]=6(a,v){5"/^"+R(v)+"(-|$)/.l("+a+")"};7 1R=18;18=6(s){5 1R(q.1Q(s))}});x.15("1j-2z",6(){D["~"]=6(r,f,t,n){7 e,i;9(i=0;(e=f[i]);i++){H(e=G(e)){8(17(e,t,n))r.z(e)}}};h["2y"]=6(e,t){t=12 1t(R(1s(t)));5 t.l(1e(e))};h["2x"]=6(e){5 e==Q(e).1H};h["2w"]=6(e){7 n,i;9(i=0;(n=e.1F[i]);i++){8(M(n)||n.1c==3)5 L}5 K};h["1N-10"]=6(e){5!G(e)};h["2v-10"]=6(e){e=e.1n;5 1r(e)==1P(e)};h["2u"]=6(e,s){7 n=x(s,Q(e));9(7 i=0;i<n.y;i++){8(n[i]==e)5 L}5 K};h["1O-10"]=6(e,a){5 1p(e,a,16)};h["1O-1N-10"]=6(e,a){5 1p(e,a,G)};h["2t"]=6(e){5 e.B==2s.2r.Z(1)};h["1M"]=6(e){5 e.1M};h["2q"]=6(e){5 e.1q===L};h["1q"]=6(e){5 e.1q};h["1L"]=6(e){5 e.1L};q.J["^="]=6(a,v){5"/^"+R(v)+"/.l("+a+")"};q.J["$="]=6(a,v){5"/"+R(v)+"$/.l("+a+")"};q.J["*="]=6(a,v){5"/"+R(v)+"/.l("+a+")"};6 1p(e,a,t){1d(a){F"n":5 K;F"2p":a="2n";1a;F"2o":a="2n+1"}7 1m=1o(e.1n);6 1k(i){7 i=(t==G)?1m.y-i:i-1;5 1m[i]==e};8(!Y(a))5 1k(a);a=a.1l("n");7 m=1K(a[0]);7 s=1K(a[1]);8((Y(m)||m==1)&&s==0)5 K;8(m==0&&!Y(s))5 1k(s);8(Y(s))s=0;7 c=1;H(e=t(e))c++;8(Y(m)||m==1)5(t==G)?(c<=s):(s>=c);5(c%m)==s}});x.15("1j-2m",6(){U=1i("L;/*@2l@8(@\\2k)U=K@2j@*/");8(!U){X=6(e,t,n){5 n?e.2i("*",t):e.X(t)};14=6(e,n){5!n||(n=="*")||(e.2h==n)};1h=1g.1I?6(e){5/1J/i.l(Q(e).1I)}:6(e){5 Q(e).1H.1f!="2g"};1e=6(e){5 e.2f||e.1G||1b(e)};6 1b(e){7 t="",n,i;9(i=0;(n=e.1F[i]);i++){1d(n.1c){F 11:F 1:t+=1b(n);1a;F 3:t+=n.2e;1a}}5 t}}});19=K;5 x}();',62,190,'|||||return|function|var|if|for||||||||pseudoClasses||||test|||this||AttributeSelector|||||||cssQuery|length|push|fr|id||selectors||case|nextElementSibling|while||tests|true|false|thisElement||replace|match|getDocument|regEscape||attributeSelectors|isMSIE|cache||getElementsByTagName|isNaN|slice|child||new|getAttribute|compareNamespace|addModule|previousElementSibling|compareTagName|parseSelector|loaded|break|_0|nodeType|switch|getTextContent|tagName|document|isXML|eval|css|_1|split|ch|parentNode|childElements|nthChild|disabled|firstElementChild|getText|RegExp|Quote|x22|PREFIX|lang|_2|arguments|else|all|links|version|se|childNodes|innerText|documentElement|contentType|xml|parseInt|indeterminate|checked|last|nth|lastElementChild|parse|_3|add|href|String|className|create|NS_IE|remove|toString|ST|select|Array|null|_4|mimeType|lastChild|firstChild|continue|modules|delete|join|caching|error|nodeValue|textContent|HTML|prefix|getElementsByTagNameNS|end|x5fwin32|cc_on|standard||odd|even|enabled|hash|location|target|not|only|empty|root|contains|level3|outerHTML|htmlFor|class|toLowerCase|Function|name|first|level2|prototype|item|scopeName|toUpperCase|ownerDocument|Document|XML|Boolean|URL|unknown|typeof|nextSibling|previousSibling|visited|link|valueOf|clearCache|catch|concat|constructor|callee|try'.split('|'),0,{}))
// -------------------------------------------------------------------
// DHTML Window Widget- By Dynamic Drive, available at: http://www.dynamicdrive.com
// v1.0: Script created Feb 15th, 07'
// v1.01: Updated to v1.01 Feb 21th, 07'
// -------------------------------------------------------------------

var dhtmlwindow={

imagefiles:['/images/min.gif', '/images/close.gif', '/images/restore.gif', '/images/resize.gif'], //Path to 4 images used by script, in that order
ajaxbustcache: true, //Bust caching when fetching a file via Ajax?

minimizeorder: 0,
tobjects: [], //object to contain references to dhtml window divs, for cleanup purposes

init:function(t){
	var domwindow=document.createElement("div") //create dhtml window div
	domwindow.id=t
	domwindow.className="dhtmlwindow"
	var domwindowdata=''
	domwindowdata='<div class="drag-handle">'
	domwindowdata+='DHTML Window <div class="drag-controls"><img src="'+this.imagefiles[0]+'" title="Minimize" /><img src="'+this.imagefiles[1]+'" title="Close" /></div>'
	domwindowdata+='</div>'
	domwindowdata+='<div class="drag-contentarea"></div>'
	domwindowdata+='<div class="drag-statusarea"><div class="drag-resizearea" style="background: transparent url('+this.imagefiles[3]+') top right no-repeat;">&nbsp;</div></div>'
	domwindowdata+='</div>'
	domwindow.innerHTML=domwindowdata
	document.getElementById("dhtmlwindowholder").appendChild(domwindow)
	this.zIndexvalue=(this.zIndexvalue)? this.zIndexvalue+1 : 100 //z-index value for DHTML window: starts at 0, increments whenever a window has focus
	var t=document.getElementById(t)
	var divs=t.getElementsByTagName("div")
	for (var i=0; i<divs.length; i++){ //go through divs inside dhtml window and extract all those with class="drag-" prefix
		if (/drag-/.test(divs[i].className))
			t[divs[i].className.replace(/drag-/, "")]=divs[i] //take out the "drag-" prefix for shorter access by name
	}
	t.style.zIndex=this.zIndexvalue //set z-index of this dhtml window
	t.handle._parent=t //store back reference to dhtml window
	t.resizearea._parent=t //same
	t.controls._parent=t //same
	t.onclose=function(){return true} //custom event handler "onclose"
	t.onmousedown=function(){dhtmlwindow.zIndexvalue++; this.style.zIndex=dhtmlwindow.zIndexvalue} //Increase z-index of window when focus is on it
	t.handle.onmousedown=dhtmlwindow.setupdrag //set up drag behavior when mouse down on handle div
	t.resizearea.onmousedown=dhtmlwindow.setupdrag //set up drag behavior when mouse down on resize div
	t.controls.onclick=dhtmlwindow.enablecontrols
	t.show=function(){dhtmlwindow.show(this)} //public function for showing dhtml window
	t.hide=function(){dhtmlwindow.close(this)} //public function for hiding dhtml window
	t.setSize=function(w, h){dhtmlwindow.setSize(this, w, h)} //public function for setting window dimensions
	t.moveTo=function(x, y){dhtmlwindow.moveTo(this, x, y)} //public function for moving dhtml window (relative to viewpoint)
	t.isResize=function(bol){dhtmlwindow.isResize(this, bol)} //public function for specifying if window is resizable
	t.isScrolling=function(bol){dhtmlwindow.isScrolling(this, bol)} //public function for specifying if window content contains scrollbars
	t.load=function(contenttype, contentsource, title){dhtmlwindow.load(this, contenttype, contentsource, title)} //public function for loading content into window
	this.tobjects[this.tobjects.length]=t
	return t //return reference to dhtml window div
},

open:function(t, contenttype, contentsource, title, attr, recalonload){
	var d=dhtmlwindow //reference dhtml window object
	function getValue(Name){
		var config=new RegExp(Name+"=([^,]+)", "i") //get name/value config pair (ie: width=400px,)
		return (config.test(attr))? parseInt(RegExp.$1) : 0 //return value portion (int), or 0 (false) if none found
	}
	if (document.getElementById(t)==null) //if window doesn't exist yet, create it
		t=this.init(t) //return reference to dhtml window div
	else
		t=document.getElementById(t)
	t.setSize(getValue(("width")), (getValue("height"))) //Set dimensions of window
	var xpos=getValue("center")? "middle" : getValue("left") //Get x coord of window
	var ypos=getValue("center")? "middle" : getValue("top") //Get y coord of window
	t.moveTo(xpos, ypos) //Position window
	if (typeof recalonload!="undefined" && recalonload=="recal" && this.scroll_top==0){ //reposition window when page fully loads with updated window viewpoints?
		if (window.attachEvent && !window.opera) //In IE, add another 400 milisecs on page load (viewpoint properties may return 0 b4 then)
			this.addEvent(window, function(){setTimeout(function(){t.moveTo(xpos, ypos)}, 400)}, "load")
		else
			this.addEvent(window, function(){t.moveTo(xpos, ypos)}, "load")
	}
	t.isResize(getValue("resize")) //Set whether window is resizable
	t.isScrolling(getValue("scrolling")) //Set whether window should contain scrollbars
	t.style.visibility="visible"
	t.style.display="block"
	t.contentarea.style.display="block"
	t.load(contenttype, contentsource, title)
	return t
},

setSize:function(t, w, h){ //set window size (min is 150px wide by 100px tall)
	t.style.width=Math.max(parseInt(w), 150)+"px"
	t.contentarea.style.height=Math.max(parseInt(h), 100)+"px"
},

moveTo:function(t, x, y){ //move window. Position includes current viewpoint of document
	this.getviewpoint() //Get current viewpoint numbers
	t.style.left=(x=="middle")? this.scroll_left+(this.docwidth-t.offsetWidth)/2+"px" : this.scroll_left+parseInt(x)+"px"
	t.style.top=(y=="middle")? this.scroll_top+(this.docheight-t.offsetHeight)/2+"px" : this.scroll_top+parseInt(y)+"px"
},

isResize:function(t, bol){ //show or hide resize inteface (part of the status bar)
	t.statusarea.style.display=(bol)? "block" : "none"
},

isScrolling:function(t, bol){ //set whether loaded content contains scrollbars
	t.contentarea.style.overflow=(bol)? "auto" : "hidden"
},

load:function(t, contenttype, contentsource, title){ //loads content into window plus set its title (3 content types: "inline", "iframe", or "ajax")
	var contenttype=contenttype.toLowerCase() //convert string to lower case
	if (typeof title!="undefined")
		t.handle.firstChild.nodeValue=title
	if (contenttype=="inline")
		t.contentarea.innerHTML=contentsource
	else if (contenttype=="div"){
		t.contentarea.innerHTML=document.getElementById(contentsource).innerHTML //Populate window with contents of specified div on page
		document.getElementById(contentsource).style.display="none" //hide that div
	}
	else if (contenttype=="iframe"){
		t.contentarea.style.overflow="hidden" //disable window scrollbars, as iframe already contains scrollbars
		if (!t.contentarea.firstChild || t.contentarea.firstChild.tagName!="IFRAME") //If iframe tag doesn't exist already, create it first
			t.contentarea.innerHTML='<iframe src="" style="margin:0; padding:0; width:100%; height: 100%" name="_iframe-'+t.id+'"></iframe>'
			window.frames["_iframe-"+t.id].location.replace(contentsource) //set location of iframe window to specified URL
		}
	else if (contenttype=="ajax"){
		this.ajax_connect(contentsource, t) //populate window with external contents fetched via Ajax
	}
	t.contentarea.datatype=contenttype //store contenttype of current window for future reference
},

setupdrag:function(e){
	var d=dhtmlwindow //reference dhtml window object
	var t=this._parent //reference dhtml window div
	d.etarget=this //remember div mouse is currently held down on ("handle" or "resize" div)
	var e=window.event || e
	d.initmousex=e.clientX //store x position of mouse onmousedown
	d.initmousey=e.clientY
	d.initx=parseInt(t.offsetLeft) //store offset x of window div onmousedown
	d.inity=parseInt(t.offsetTop)
	d.width=parseInt(t.offsetWidth) //store width of window div
	d.contentheight=parseInt(t.contentarea.offsetHeight) //store height of window div's content div
	if (t.contentarea.datatype=="iframe"){ //if content of this window div is "iframe"
		t.style.backgroundColor="#F8F8F8" //colorize and hide content div (while window is being dragged)
		t.contentarea.style.visibility="hidden"
	}
	document.onmousemove=d.getdistance //get distance travelled by mouse as it moves
	document.onmouseup=function(){
		if (t.contentarea.datatype=="iframe"){ //restore color and visibility of content div onmouseup
			t.contentarea.style.backgroundColor="white"
			t.contentarea.style.visibility="visible"
		}
	d.stop()
	}
	return false
},

getdistance:function(e){
	var d=dhtmlwindow
	var etarget=d.etarget
	var e=window.event || e
	d.distancex=e.clientX-d.initmousex //horizontal distance travelled relative to starting point
	d.distancey=e.clientY-d.initmousey
	if (etarget.className=="drag-handle") //if target element is "handle" div
		d.move(etarget._parent, e)
	else if (etarget.className=="drag-resizearea") //if target element is "resize" div
		d.resize(etarget._parent, e)
	return false //cancel default dragging behavior
},

getviewpoint:function(){ //get window viewpoint numbers
	var ie=document.all && !window.opera
	var domclientWidth=document.documentElement && parseInt(document.documentElement.clientWidth) || 100000 //Preliminary doc width in non IE browsers
	this.standardbody=(document.compatMode=="CSS1Compat")? document.documentElement : document.body //create reference to common "body" across doctypes
	this.scroll_top=(ie)? this.standardbody.scrollTop : window.pageYOffset
	this.scroll_left=(ie)? this.standardbody.scrollLeft : window.pageXOffset
	this.docwidth=(ie)? this.standardbody.clientWidth : (/Safari/i.test(navigator.userAgent))? window.innerWidth : Math.min(domclientWidth, window.innerWidth-16)
	this.docheight=(ie)? this.standardbody.clientHeight: window.innerHeight
},

rememberattrs:function(t){ //remember certain attributes of the window when it's minimized or closed, such as dimensions, position on page
	this.getviewpoint() //Get current window viewpoint numbers
	t.lastx=parseInt((t.style.left || t.offsetLeft))-dhtmlwindow.scroll_left //store last known x coord of window just before minimizing
	t.lasty=parseInt((t.style.top || t.offsetTop))-dhtmlwindow.scroll_top
	t.lastwidth=t.style.width //store last known width of window just before minimizing
},

move:function(t, e){
	t.style.left=dhtmlwindow.distancex+dhtmlwindow.initx+"px"
	t.style.top=dhtmlwindow.distancey+dhtmlwindow.inity+"px"
},

resize:function(t, e){
	t.style.width=Math.max(dhtmlwindow.width+dhtmlwindow.distancex, 150)+"px"
	t.contentarea.style.height=Math.max(dhtmlwindow.contentheight+dhtmlwindow.distancey, 100)+"px"
},

enablecontrols:function(e){
	var d=dhtmlwindow
	var sourceobj=window.event? window.event.srcElement : e.target //Get element within "handle" div mouse is currently on (the controls)
	if (/Minimize/i.test(sourceobj.getAttribute("title"))) //if this is the "minimize" control
		d.minimize(sourceobj, this._parent)
	else if (/Restore/i.test(sourceobj.getAttribute("title"))) //if this is the "restore" control
		d.restore(sourceobj, this._parent)
	else if (/Close/i.test(sourceobj.getAttribute("title"))) //if this is the "close" control
		d.close(this._parent)
	return false
},

minimize:function(button, t){
	dhtmlwindow.rememberattrs(t)
	button.setAttribute("src", dhtmlwindow.imagefiles[2])
	button.setAttribute("title", "Restore")
	t.state="minimized" //indicate the state of the window as being "minimized"
	t.contentarea.style.display="none"
	t.statusarea.style.display="none"
	if (typeof t.minimizeorder=="undefined"){ //stack order of minmized window on screen relative to any other minimized windows
		dhtmlwindow.minimizeorder++ //increment order
		t.minimizeorder=dhtmlwindow.minimizeorder
	}
	t.style.left="10px" //left coord of minmized window
	t.style.width="200px"
	var windowspacing=t.minimizeorder*10 //spacing (gap) between each minmized window(s)
	t.style.top=dhtmlwindow.scroll_top+dhtmlwindow.docheight-(t.handle.offsetHeight*t.minimizeorder)-windowspacing+"px"
},

restore:function(button, t){
	dhtmlwindow.getviewpoint()
	button.setAttribute("src", dhtmlwindow.imagefiles[0])
	button.setAttribute("title", "Minimize")
	t.state="fullview" //indicate the state of the window as being "fullview"
	t.style.display="block"
	t.contentarea.style.display="block"
	t.statusarea.style.display="block"
	t.style.left=parseInt(t.lastx)+dhtmlwindow.scroll_left+"px" //position window to last known x coord just before minimizing
	t.style.top=parseInt(t.lasty)+dhtmlwindow.scroll_top+"px"
	t.style.width=parseInt(t.lastwidth)+"px"
},


close:function(t){
	try{
		var closewinbol=t.onclose()
	}
	catch(err){ //In non IE browsers, all errors are caught, so just run the below
		alert(err)
		var closewinbol=true
 }
	finally{ //In IE, not all errors are caught, so check if variable isn't defined in IE in those cases
		if (typeof closewinbol=="undefined"){
			alert("An error has occured somwhere inside your \"onclose\" event handler")
			var closewinbol=true
		}
	}
	if (closewinbol){ //if custom event handler function returns true
		if (t.state!="minimized") //if this window isn't currently minimized
			dhtmlwindow.rememberattrs(t) //remember window's dimensions/position on the page before closing
		t.style.display="none"
	}
	return closewinbol
},

show:function(t){
	if (t.lastx) //If there exists previously stored information such as last x position on window attributes (meaning it's been minimized or closed)
		dhtmlwindow.restore(t.controls.firstChild, t) //restore the window using that info
	else
		t.style.display="block"
	t.state="fullview" //indicate the state of the window as being "fullview"
},

ajax_connect:function(url, t){
	var page_request = false
	var bustcacheparameter=""
	if (window.XMLHttpRequest) // if Mozilla, IE7, Safari etc
		page_request = new XMLHttpRequest()
	else if (window.ActiveXObject){ // if IE6 or below
		try {
		page_request = new ActiveXObject("Msxml2.XMLHTTP")
		}
		catch (e){
			try{
			page_request = new ActiveXObject("Microsoft.XMLHTTP")
			}
			catch (e){}
		}
	}
	else
		return false
	page_request.onreadystatechange=function(){dhtmlwindow.ajax_loadpage(page_request, t)}
	if (this.ajaxbustcache) //if bust caching of external page
		bustcacheparameter=(url.indexOf("?")!=-1)? "&"+new Date().getTime() : "?"+new Date().getTime()
	page_request.open('GET', url+bustcacheparameter, true)
	page_request.send(null)
},

ajax_loadpage:function(page_request, t){
	if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)){
	t.contentarea.innerHTML=page_request.responseText
	}
},


stop:function(){
	dhtmlwindow.etarget=null //clean up
	document.onmousemove=null
	document.onmouseup=null
},

addEvent:function(target, functionref, tasktype){ //assign a function to execute to an event handler (ie: onunload)
	var tasktype=(window.addEventListener)? tasktype : "on"+tasktype
	if (target.addEventListener)
		target.addEventListener(tasktype, functionref, false)
	else if (target.attachEvent)
		target.attachEvent(tasktype, functionref)
},

cleanup:function(){
	for (var i=0; i<dhtmlwindow.tobjects.length; i++){
		dhtmlwindow.tobjects[i].handle._parent=dhtmlwindow.tobjects[i].resizearea._parent=dhtmlwindow.tobjects[i].controls._parent=null
	}
	window.onload=null
}

} //End dhtmlwindow object

document.write('<div id="dhtmlwindowholder"><span style="display:none">.</span></div>') //container that holds all dhtml window divs on page
window.onunload=dhtmlwindow.cleanup
/*  Copyright Mihai Bazon, 2002-2005  |  www.bazon.net/mishoo
 * -----------------------------------------------------------
 *
 * The DHTML Calendar, version 1.0 "It is happening again"
 *
 * Details and latest version at:
 * www.dynarch.com/projects/calendar
 *
 * This script is developed by Dynarch.com.  Visit us at www.dynarch.com.
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 */

// $Id: calendar.js,v 1.51 2005/03/07 16:44:31 mishoo Exp $

/** The Calendar object constructor. */
Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
	// member variables
	this.activeDiv = null;
	this.currentDateEl = null;
	this.getDateStatus = null;
	this.getDateToolTip = null;
	this.getDateText = null;
	this.timeout = null;
	this.onSelected = onSelected || null;
	this.onClose = onClose || null;
	this.dragging = false;
	this.hidden = false;
	this.minYear = 1970;
	this.maxYear = 2050;
	this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
	this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
	this.isPopup = true;
	this.weekNumbers = true;
	this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
	this.showsOtherMonths = false;
	this.dateStr = dateStr;
	this.ar_days = null;
	this.showsTime = false;
	this.time24 = true;
	this.yearStep = 2;
	this.hiliteToday = true;
	this.multiple = null;
	// HTML elements
	this.table = null;
	this.element = null;
	this.tbody = null;
	this.firstdayname = null;
	// Combo boxes
	this.monthsCombo = null;
	this.yearsCombo = null;
	this.hilitedMonth = null;
	this.activeMonth = null;
	this.hilitedYear = null;
	this.activeYear = null;
	// Information
	this.dateClicked = false;

	// one-time initializations
	if (typeof Calendar._SDN == "undefined") {
		// table of short day names
		if (typeof Calendar._SDN_len == "undefined")
			Calendar._SDN_len = 3;
		var ar = new Array();
		for (var i = 8; i > 0;) {
			ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
		}
		Calendar._SDN = ar;
		// table of short month names
		if (typeof Calendar._SMN_len == "undefined")
			Calendar._SMN_len = 3;
		ar = new Array();
		for (var i = 12; i > 0;) {
			ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
		}
		Calendar._SMN = ar;
	}
};

// ** constants

/// "static", needed for event handlers.
Calendar._C = null;

/// detect a special case of "web browser"
Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
		   !/opera/i.test(navigator.userAgent) );

Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

/// detect Opera browser
Calendar.is_opera = /opera/i.test(navigator.userAgent);

/// detect KHTML-based browsers
Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);

// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
//        library, at some point.

Calendar.getAbsolutePos = function(el) {
	var SL = 0, ST = 0;
	var is_div = /^div$/i.test(el.tagName);
	if (is_div && el.scrollLeft)
		SL = el.scrollLeft;
	if (is_div && el.scrollTop)
		ST = el.scrollTop;
	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
	if (el.offsetParent) {
		var tmp = this.getAbsolutePos(el.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}
	return r;
};

Calendar.isRelated = function (el, evt) {
	var related = evt.relatedTarget;
	if (!related) {
		var type = evt.type;
		if (type == "mouseover") {
			related = evt.fromElement;
		} else if (type == "mouseout") {
			related = evt.toElement;
		}
	}
	while (related) {
		if (related == el) {
			return true;
		}
		related = related.parentNode;
	}
	return false;
};

Calendar.removeClass = function(el, className) {
	if (!(el && el.className)) {
		return;
	}
	var cls = el.className.split(" ");
	var ar = new Array();
	for (var i = cls.length; i > 0;) {
		if (cls[--i] != className) {
			ar[ar.length] = cls[i];
		}
	}
	el.className = ar.join(" ");
};

Calendar.addClass = function(el, className) {
	Calendar.removeClass(el, className);
	el.className += " " + className;
};

// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
Calendar.getElement = function(ev) {
	var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
	while (f.nodeType != 1 || /^div$/i.test(f.tagName))
		f = f.parentNode;
	return f;
};

Calendar.getTargetElement = function(ev) {
	var f = Calendar.is_ie ? window.event.srcElement : ev.target;
	while (f.nodeType != 1)
		f = f.parentNode;
	return f;
};

Calendar.stopEvent = function(ev) {
	ev || (ev = window.event);
	if (Calendar.is_ie) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
	return false;
};

Calendar.addEvent = function(el, evname, func) {
	if (el.attachEvent) { // IE
		el.attachEvent("on" + evname, func);
	} else if (el.addEventListener) { // Gecko / W3C
		el.addEventListener(evname, func, true);
	} else {
		el["on" + evname] = func;
	}
};

Calendar.removeEvent = function(el, evname, func) {
	if (el.detachEvent) { // IE
		el.detachEvent("on" + evname, func);
	} else if (el.removeEventListener) { // Gecko / W3C
		el.removeEventListener(evname, func, true);
	} else {
		el["on" + evname] = null;
	}
};

Calendar.createElement = function(type, parent) {
	var el = null;
	if (document.createElementNS) {
		// use the XHTML namespace; IE won't normally get here unless
		// _they_ "fix" the DOM2 implementation.
		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
	} else {
		el = document.createElement(type);
	}
	if (typeof parent != "undefined") {
		parent.appendChild(el);
	}
	return el;
};

// END: UTILITY FUNCTIONS

// BEGIN: CALENDAR STATIC FUNCTIONS

/** Internal -- adds a set of events to make some element behave like a button. */
Calendar._add_evs = function(el) {
	with (Calendar) {
		addEvent(el, "mouseover", dayMouseOver);
		addEvent(el, "mousedown", dayMouseDown);
		addEvent(el, "mouseout", dayMouseOut);
		if (is_ie) {
			addEvent(el, "dblclick", dayMouseDblClick);
			el.setAttribute("unselectable", true);
		}
	}
};

Calendar.findMonth = function(el) {
	if (typeof el.month != "undefined") {
		return el;
	} else if (typeof el.parentNode.month != "undefined") {
		return el.parentNode;
	}
	return null;
};

Calendar.findYear = function(el) {
	if (typeof el.year != "undefined") {
		return el;
	} else if (typeof el.parentNode.year != "undefined") {
		return el.parentNode;
	}
	return null;
};

Calendar.showMonthsCombo = function () {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	var cal = cal;
	var cd = cal.activeDiv;
	var mc = cal.monthsCombo;
	if (cal.hilitedMonth) {
		Calendar.removeClass(cal.hilitedMonth, "hilite");
	}
	if (cal.activeMonth) {
		Calendar.removeClass(cal.activeMonth, "active");
	}
	var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
	Calendar.addClass(mon, "active");
	cal.activeMonth = mon;
	var s = mc.style;
	s.display = "block";
	if (cd.navtype < 0)
		s.left = cd.offsetLeft + "px";
	else {
		var mcw = mc.offsetWidth;
		if (typeof mcw == "undefined")
			// Konqueror brain-dead techniques
			mcw = 50;
		s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
	}
	s.top = (cd.offsetTop + cd.offsetHeight) + "px";
};

Calendar.showYearsCombo = function (fwd) {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	var cal = cal;
	var cd = cal.activeDiv;
	var yc = cal.yearsCombo;
	if (cal.hilitedYear) {
		Calendar.removeClass(cal.hilitedYear, "hilite");
	}
	if (cal.activeYear) {
		Calendar.removeClass(cal.activeYear, "active");
	}
	cal.activeYear = null;
	var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
	var yr = yc.firstChild;
	var show = false;
	for (var i = 12; i > 0; --i) {
		if (Y >= cal.minYear && Y <= cal.maxYear) {
			yr.innerHTML = Y;
			yr.year = Y;
			yr.style.display = "block";
			show = true;
		} else {
			yr.style.display = "none";
		}
		yr = yr.nextSibling;
		Y += fwd ? cal.yearStep : -cal.yearStep;
	}
	if (show) {
		var s = yc.style;
		s.display = "block";
		if (cd.navtype < 0)
			s.left = cd.offsetLeft + "px";
		else {
			var ycw = yc.offsetWidth;
			if (typeof ycw == "undefined")
				// Konqueror brain-dead techniques
				ycw = 50;
			s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
		}
		s.top = (cd.offsetTop + cd.offsetHeight) + "px";
	}
};

// event handlers

Calendar.tableMouseUp = function(ev) {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	if (cal.timeout) {
		clearTimeout(cal.timeout);
	}
	var el = cal.activeDiv;
	if (!el) {
		return false;
	}
	var target = Calendar.getTargetElement(ev);
	ev || (ev = window.event);
	Calendar.removeClass(el, "active");
	if (target == el || target.parentNode == el) {
		Calendar.cellClick(el, ev);
	}
	var mon = Calendar.findMonth(target);
	var date = null;
	if (mon) {
		date = new Date(cal.date);
		if (mon.month != date.getMonth()) {
			date.setMonth(mon.month);
			cal.setDate(date);
			cal.dateClicked = false;
			cal.callHandler();
		}
	} else {
		var year = Calendar.findYear(target);
		if (year) {
			date = new Date(cal.date);
			if (year.year != date.getFullYear()) {
				date.setFullYear(year.year);
				cal.setDate(date);
				cal.dateClicked = false;
				cal.callHandler();
			}
		}
	}
	with (Calendar) {
		removeEvent(document, "mouseup", tableMouseUp);
		removeEvent(document, "mouseover", tableMouseOver);
		removeEvent(document, "mousemove", tableMouseOver);
		cal._hideCombos();
		_C = null;
		return stopEvent(ev);
	}
};

Calendar.tableMouseOver = function (ev) {
	var cal = Calendar._C;
	if (!cal) {
		return;
	}
	var el = cal.activeDiv;
	var target = Calendar.getTargetElement(ev);
	if (target == el || target.parentNode == el) {
		Calendar.addClass(el, "hilite active");
		Calendar.addClass(el.parentNode, "rowhilite");
	} else {
		if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
			Calendar.removeClass(el, "active");
		Calendar.removeClass(el, "hilite");
		Calendar.removeClass(el.parentNode, "rowhilite");
	}
	ev || (ev = window.event);
	if (el.navtype == 50 && target != el) {
		var pos = Calendar.getAbsolutePos(el);
		var w = el.offsetWidth;
		var x = ev.clientX;
		var dx;
		var decrease = true;
		if (x > pos.x + w) {
			dx = x - pos.x - w;
			decrease = false;
		} else
			dx = pos.x - x;

		if (dx < 0) dx = 0;
		var range = el._range;
		var current = el._current;
		var count = Math.floor(dx / 10) % range.length;
		for (var i = range.length; --i >= 0;)
			if (range[i] == current)
				break;
		while (count-- > 0)
			if (decrease) {
				if (--i < 0)
					i = range.length - 1;
			} else if ( ++i >= range.length )
				i = 0;
		var newval = range[i];
		el.innerHTML = newval;

		cal.onUpdateTime();
	}
	var mon = Calendar.findMonth(target);
	if (mon) {
		if (mon.month != cal.date.getMonth()) {
			if (cal.hilitedMonth) {
				Calendar.removeClass(cal.hilitedMonth, "hilite");
			}
			Calendar.addClass(mon, "hilite");
			cal.hilitedMonth = mon;
		} else if (cal.hilitedMonth) {
			Calendar.removeClass(cal.hilitedMonth, "hilite");
		}
	} else {
		if (cal.hilitedMonth) {
			Calendar.removeClass(cal.hilitedMonth, "hilite");
		}
		var year = Calendar.findYear(target);
		if (year) {
			if (year.year != cal.date.getFullYear()) {
				if (cal.hilitedYear) {
					Calendar.removeClass(cal.hilitedYear, "hilite");
				}
				Calendar.addClass(year, "hilite");
				cal.hilitedYear = year;
			} else if (cal.hilitedYear) {
				Calendar.removeClass(cal.hilitedYear, "hilite");
			}
		} else if (cal.hilitedYear) {
			Calendar.removeClass(cal.hilitedYear, "hilite");
		}
	}
	return Calendar.stopEvent(ev);
};

Calendar.tableMouseDown = function (ev) {
	if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
		return Calendar.stopEvent(ev);
	}
};

Calendar.calDragIt = function (ev) {
	var cal = Calendar._C;
	if (!(cal && cal.dragging)) {
		return false;
	}
	var posX;
	var posY;
	if (Calendar.is_ie) {
		posY = window.event.clientY + document.body.scrollTop;
		posX = window.event.clientX + document.body.scrollLeft;
	} else {
		posX = ev.pageX;
		posY = ev.pageY;
	}
	cal.hideShowCovered();
	var st = cal.element.style;
	st.left = (posX - cal.xOffs) + "px";
	st.top = (posY - cal.yOffs) + "px";
	return Calendar.stopEvent(ev);
};

Calendar.calDragEnd = function (ev) {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	cal.dragging = false;
	with (Calendar) {
		removeEvent(document, "mousemove", calDragIt);
		removeEvent(document, "mouseup", calDragEnd);
		tableMouseUp(ev);
	}
	cal.hideShowCovered();
};

Calendar.dayMouseDown = function(ev) {
	var el = Calendar.getElement(ev);
	if (el.disabled) {
		return false;
	}
	var cal = el.calendar;
	cal.activeDiv = el;
	Calendar._C = cal;
	if (el.navtype != 300) with (Calendar) {
		if (el.navtype == 50) {
			el._current = el.innerHTML;
			addEvent(document, "mousemove", tableMouseOver);
		} else
			addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
		addClass(el, "hilite active");
		addEvent(document, "mouseup", tableMouseUp);
	} else if (cal.isPopup) {
		cal._dragStart(ev);
	}
	if (el.navtype == -1 || el.navtype == 1) {
		if (cal.timeout) clearTimeout(cal.timeout);
		cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
	} else if (el.navtype == -2 || el.navtype == 2) {
		if (cal.timeout) clearTimeout(cal.timeout);
		cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
	} else {
		cal.timeout = null;
	}
	return Calendar.stopEvent(ev);
};

Calendar.dayMouseDblClick = function(ev) {
	Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
	if (Calendar.is_ie) {
		document.selection.empty();
	}
};

Calendar.dayMouseOver = function(ev) {
	var el = Calendar.getElement(ev);
	if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
		return false;
	}
	if (el.ttip) {
		if (el.ttip.substr(0, 1) == "_") {
			el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
		}
		el.calendar.tooltips.innerHTML = el.ttip;
	}
	if (el.navtype != 300) {
		Calendar.addClass(el, "hilite");
		if (el.caldate) {
			Calendar.addClass(el.parentNode, "rowhilite");
		}
	}
	return Calendar.stopEvent(ev);
};

Calendar.dayMouseOut = function(ev) {
	with (Calendar) {
		var el = getElement(ev);
		if (isRelated(el, ev) || _C || el.disabled)
			return false;
		removeClass(el, "hilite");
		if (el.caldate)
			removeClass(el.parentNode, "rowhilite");
		if (el.calendar)
			el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
		return stopEvent(ev);
	}
};

/**
 *  A generic "click" handler :) handles all types of buttons defined in this
 *  calendar.
 */
Calendar.cellClick = function(el, ev) {
	var cal = el.calendar;
	var closing = false;
	var newdate = false;
	var date = null;
	if (typeof el.navtype == "undefined") {
		if (cal.currentDateEl) {
			Calendar.removeClass(cal.currentDateEl, "selected");
			Calendar.addClass(el, "selected");
			closing = (cal.currentDateEl == el);
			if (!closing) {
				cal.currentDateEl = el;
			}
		}
		cal.date.setDateOnly(el.caldate);
		date = cal.date;
		var other_month = !(cal.dateClicked = !el.otherMonth);
		if (!other_month && !cal.currentDateEl)
			cal._toggleMultipleDate(new Date(date));
		else
			newdate = !el.disabled;
		// a date was clicked
		if (other_month)
			cal._init(cal.firstDayOfWeek, date);
	} else {
		if (el.navtype == 200) {
			Calendar.removeClass(el, "hilite");
			cal.callCloseHandler();
			return;
		}
		date = new Date(cal.date);
		if (el.navtype == 0)
			date.setDateOnly(new Date()); // TODAY
		// unless "today" was clicked, we assume no date was clicked so
		// the selected handler will know not to close the calenar when
		// in single-click mode.
		// cal.dateClicked = (el.navtype == 0);
		cal.dateClicked = false;
		var year = date.getFullYear();
		var mon = date.getMonth();
		function setMonth(m) {
			var day = date.getDate();
			var max = date.getMonthDays(m);
			if (day > max) {
				date.setDate(max);
			}
			date.setMonth(m);
		};
		switch (el.navtype) {
		    case 400:
			Calendar.removeClass(el, "hilite");
			var text = Calendar._TT["ABOUT"];
			if (typeof text != "undefined") {
				text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
			} else {
				// FIXME: this should be removed as soon as lang files get updated!
				text = "Help and about box text is not translated into this language.\n" +
					"If you know this language and you feel generous please update\n" +
					"the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
					"and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n" +
					"Thank you!\n" +
					"http://dynarch.com/mishoo/calendar.epl\n";
			}
			alert(text);
			return;
		    case -2:
			if (year > cal.minYear) {
				date.setFullYear(year - 1);
			}
			break;
		    case -1:
			if (mon > 0) {
				setMonth(mon - 1);
			} else if (year-- > cal.minYear) {
				date.setFullYear(year);
				setMonth(11);
			}
			break;
		    case 1:
			if (mon < 11) {
				setMonth(mon + 1);
			} else if (year < cal.maxYear) {
				date.setFullYear(year + 1);
				setMonth(0);
			}
			break;
		    case 2:
			if (year < cal.maxYear) {
				date.setFullYear(year + 1);
			}
			break;
		    case 100:
			cal.setFirstDayOfWeek(el.fdow);
			return;
		    case 50:
			var range = el._range;
			var current = el.innerHTML;
			for (var i = range.length; --i >= 0;)
				if (range[i] == current)
					break;
			if (ev && ev.shiftKey) {
				if (--i < 0)
					i = range.length - 1;
			} else if ( ++i >= range.length )
				i = 0;
			var newval = range[i];
			el.innerHTML = newval;
			cal.onUpdateTime();
			return;
		    case 0:
			// TODAY will bring us here
			if ((typeof cal.getDateStatus == "function") &&
			    cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
				return false;
			}
			break;
		}
		if (!date.equalsTo(cal.date)) {
			cal.setDate(date);
			newdate = true;
		} else if (el.navtype == 0)
			newdate = closing = true;
	}
	if (newdate) {
		ev && cal.callHandler();
	}
	if (closing) {
		Calendar.removeClass(el, "hilite");
		ev && cal.callCloseHandler();
	}
};

// END: CALENDAR STATIC FUNCTIONS

// BEGIN: CALENDAR OBJECT FUNCTIONS

/**
 *  This function creates the calendar inside the given parent.  If _par is
 *  null than it creates a popup calendar inside the BODY element.  If _par is
 *  an element, be it BODY, then it creates a non-popup calendar (still
 *  hidden).  Some properties need to be set before calling this function.
 */
Calendar.prototype.create = function (_par) {
	var parent = null;
	if (! _par) {
		// default parent is the document body, in which case we create
		// a popup calendar.
		parent = document.getElementsByTagName("body")[0];
		this.isPopup = true;
	} else {
		parent = _par;
		this.isPopup = false;
	}
	this.date = this.dateStr ? new Date(this.dateStr) : new Date();

	var table = Calendar.createElement("table");
	this.table = table;
	table.cellSpacing = 0;
	table.cellPadding = 0;
	table.calendar = this;
	Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);

	var div = Calendar.createElement("div");
	this.element = div;
	div.className = "calendar";
	if (this.isPopup) {
		div.style.position = "absolute";
		div.style.display = "none";
	}
	div.appendChild(table);

	var thead = Calendar.createElement("thead", table);
	var cell = null;
	var row = null;

	var cal = this;
	var hh = function (text, cs, navtype) {
		cell = Calendar.createElement("td", row);
		cell.colSpan = cs;
		cell.className = "button";
		if (navtype != 0 && Math.abs(navtype) <= 2)
			cell.className += " nav";
		Calendar._add_evs(cell);
		cell.calendar = cal;
		cell.navtype = navtype;
		cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
		return cell;
	};

	row = Calendar.createElement("tr", thead);
	var title_length = 6;
	(this.isPopup) && --title_length;
	(this.weekNumbers) && ++title_length;

	hh("?", 1, 400).ttip = Calendar._TT["INFO"];
	this.title = hh("", title_length, 300);
	this.title.className = "title";
	if (this.isPopup) {
		this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
		this.title.style.cursor = "move";
		hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
	}

	row = Calendar.createElement("tr", thead);
	row.className = "headrow";

	this._nav_py = hh("&#x00ab;", 1, -2);
	this._nav_py.ttip = Calendar._TT["PREV_YEAR"];

	this._nav_pm = hh("&#x2039;", 1, -1);
	this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];

	this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
	this._nav_now.ttip = Calendar._TT["GO_TODAY"];

	this._nav_nm = hh("&#x203a;", 1, 1);
	this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];

	this._nav_ny = hh("&#x00bb;", 1, 2);
	this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];

	// day names
	row = Calendar.createElement("tr", thead);
	row.className = "daynames";
	if (this.weekNumbers) {
		cell = Calendar.createElement("td", row);
		cell.className = "name wn";
		cell.innerHTML = Calendar._TT["WK"];
	}
	for (var i = 7; i > 0; --i) {
		cell = Calendar.createElement("td", row);
		if (!i) {
			cell.navtype = 100;
			cell.calendar = this;
			Calendar._add_evs(cell);
		}
	}
	this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
	this._displayWeekdays();

	var tbody = Calendar.createElement("tbody", table);
	this.tbody = tbody;

	for (i = 6; i > 0; --i) {
		row = Calendar.createElement("tr", tbody);
		if (this.weekNumbers) {
			cell = Calendar.createElement("td", row);
		}
		for (var j = 7; j > 0; --j) {
			cell = Calendar.createElement("td", row);
			cell.calendar = this;
			Calendar._add_evs(cell);
		}
	}

	if (this.showsTime) {
		row = Calendar.createElement("tr", tbody);
		row.className = "time";

		cell = Calendar.createElement("td", row);
		cell.className = "time";
		cell.colSpan = 2;
		cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";

		cell = Calendar.createElement("td", row);
		cell.className = "time";
		cell.colSpan = this.weekNumbers ? 4 : 3;

		(function(){
			function makeTimePart(className, init, range_start, range_end) {
				var part = Calendar.createElement("span", cell);
				part.className = className;
				part.innerHTML = init;
				part.calendar = cal;
				part.ttip = Calendar._TT["TIME_PART"];
				part.navtype = 50;
				part._range = [];
				if (typeof range_start != "number")
					part._range = range_start;
				else {
					for (var i = range_start; i <= range_end; ++i) {
						var txt;
						if (i < 10 && range_end >= 10) txt = '0' + i;
						else txt = '' + i;
						part._range[part._range.length] = txt;
					}
				}
				Calendar._add_evs(part);
				return part;
			};
			var hrs = cal.date.getHours();
			var mins = cal.date.getMinutes();
			var t12 = !cal.time24;
			var pm = (hrs > 12);
			if (t12 && pm) hrs -= 12;
			var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
			var span = Calendar.createElement("span", cell);
			span.innerHTML = ":";
			span.className = "colon";
			var M = makeTimePart("minute", mins, 0, 59);
			var AP = null;
			cell = Calendar.createElement("td", row);
			cell.className = "time";
			cell.colSpan = 2;
			if (t12)
				AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
			else
				cell.innerHTML = "&nbsp;";

			cal.onSetTime = function() {
				var pm, hrs = this.date.getHours(),
					mins = this.date.getMinutes();
				if (t12) {
					pm = (hrs >= 12);
					if (pm) hrs -= 12;
					if (hrs == 0) hrs = 12;
					AP.innerHTML = pm ? "pm" : "am";
				}
				H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
				M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
			};

			cal.onUpdateTime = function() {
				var date = this.date;
				var h = parseInt(H.innerHTML, 10);
				if (t12) {
					if (/pm/i.test(AP.innerHTML) && h < 12)
						h += 12;
					else if (/am/i.test(AP.innerHTML) && h == 12)
						h = 0;
				}
				var d = date.getDate();
				var m = date.getMonth();
				var y = date.getFullYear();
				date.setHours(h);
				date.setMinutes(parseInt(M.innerHTML, 10));
				date.setFullYear(y);
				date.setMonth(m);
				date.setDate(d);
				this.dateClicked = false;
				this.callHandler();
			};
		})();
	} else {
		this.onSetTime = this.onUpdateTime = function() {};
	}

	var tfoot = Calendar.createElement("tfoot", table);

	row = Calendar.createElement("tr", tfoot);
	row.className = "footrow";

	cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
	cell.className = "ttip";
	if (this.isPopup) {
		cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
		cell.style.cursor = "move";
	}
	this.tooltips = cell;

	div = Calendar.createElement("div", this.element);
	this.monthsCombo = div;
	div.className = "combo";
	for (i = 0; i < Calendar._MN.length; ++i) {
		var mn = Calendar.createElement("div");
		mn.className = Calendar.is_ie ? "label-IEfix" : "label";
		mn.month = i;
		mn.innerHTML = Calendar._SMN[i];
		div.appendChild(mn);
	}

	div = Calendar.createElement("div", this.element);
	this.yearsCombo = div;
	div.className = "combo";
	for (i = 12; i > 0; --i) {
		var yr = Calendar.createElement("div");
		yr.className = Calendar.is_ie ? "label-IEfix" : "label";
		div.appendChild(yr);
	}

	this._init(this.firstDayOfWeek, this.date);
	parent.appendChild(this.element);
};

/** keyboard navigation, only for popup calendars */
Calendar._keyEvent = function(ev) {
	var cal = window._dynarch_popupCalendar;
	if (!cal || cal.multiple)
		return false;
	(Calendar.is_ie) && (ev = window.event);
	var act = (Calendar.is_ie || ev.type == "keypress"),
		K = ev.keyCode;
	if (ev.ctrlKey) {
		switch (K) {
		    case 37: // KEY left
			act && Calendar.cellClick(cal._nav_pm);
			break;
		    case 38: // KEY up
			act && Calendar.cellClick(cal._nav_py);
			break;
		    case 39: // KEY right
			act && Calendar.cellClick(cal._nav_nm);
			break;
		    case 40: // KEY down
			act && Calendar.cellClick(cal._nav_ny);
			break;
		    default:
			return false;
		}
	} else switch (K) {
	    case 32: // KEY space (now)
		Calendar.cellClick(cal._nav_now);
		break;
	    case 27: // KEY esc
		act && cal.callCloseHandler();
		break;
	    case 37: // KEY left
	    case 38: // KEY up
	    case 39: // KEY right
	    case 40: // KEY down
		if (act) {
			var prev, x, y, ne, el, step;
			prev = K == 37 || K == 38;
			step = (K == 37 || K == 39) ? 1 : 7;
			function setVars() {
				el = cal.currentDateEl;
				var p = el.pos;
				x = p & 15;
				y = p >> 4;
				ne = cal.ar_days[y][x];
			};setVars();
			function prevMonth() {
				var date = new Date(cal.date);
				date.setDate(date.getDate() - step);
				cal.setDate(date);
			};
			function nextMonth() {
				var date = new Date(cal.date);
				date.setDate(date.getDate() + step);
				cal.setDate(date);
			};
			while (1) {
				switch (K) {
				    case 37: // KEY left
					if (--x >= 0)
						ne = cal.ar_days[y][x];
					else {
						x = 6;
						K = 38;
						continue;
					}
					break;
				    case 38: // KEY up
					if (--y >= 0)
						ne = cal.ar_days[y][x];
					else {
						prevMonth();
						setVars();
					}
					break;
				    case 39: // KEY right
					if (++x < 7)
						ne = cal.ar_days[y][x];
					else {
						x = 0;
						K = 40;
						continue;
					}
					break;
				    case 40: // KEY down
					if (++y < cal.ar_days.length)
						ne = cal.ar_days[y][x];
					else {
						nextMonth();
						setVars();
					}
					break;
				}
				break;
			}
			if (ne) {
				if (!ne.disabled)
					Calendar.cellClick(ne);
				else if (prev)
					prevMonth();
				else
					nextMonth();
			}
		}
		break;
	    case 13: // KEY enter
		if (act)
			Calendar.cellClick(cal.currentDateEl, ev);
		break;
	    default:
		return false;
	}
	return Calendar.stopEvent(ev);
};

/**
 *  (RE)Initializes the calendar to the given date and firstDayOfWeek
 */
Calendar.prototype._init = function (firstDayOfWeek, date) {
	var today = new Date(),
		TY = today.getFullYear(),
		TM = today.getMonth(),
		TD = today.getDate();
	this.table.style.visibility = "hidden";
	var year = date.getFullYear();
	if (year < this.minYear) {
		year = this.minYear;
		date.setFullYear(year);
	} else if (year > this.maxYear) {
		year = this.maxYear;
		date.setFullYear(year);
	}
	this.firstDayOfWeek = firstDayOfWeek;
	this.date = new Date(date);
	var month = date.getMonth();
	var mday = date.getDate();
	var no_days = date.getMonthDays();

	// calendar voodoo for computing the first day that would actually be
	// displayed in the calendar, even if it's from the previous month.
	// WARNING: this is magic. ;-)
	date.setDate(1);
	var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
	if (day1 < 0)
		day1 += 7;
	date.setDate(-day1);
	date.setDate(date.getDate() + 1);

	var row = this.tbody.firstChild;
	var MN = Calendar._SMN[month];
	var ar_days = this.ar_days = new Array();
	var weekend = Calendar._TT["WEEKEND"];
	var dates = this.multiple ? (this.datesCells = {}) : null;
	for (var i = 0; i < 6; ++i, row = row.nextSibling) {
		var cell = row.firstChild;
		if (this.weekNumbers) {
			cell.className = "day wn";
			cell.innerHTML = date.getWeekNumber();
			cell = cell.nextSibling;
		}
		row.className = "daysrow";
		var hasdays = false, iday, dpos = ar_days[i] = [];
		for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
			iday = date.getDate();
			var wday = date.getDay();
			cell.className = "day";
			cell.pos = i << 4 | j;
			dpos[j] = cell;
			var current_month = (date.getMonth() == month);
			if (!current_month) {
				if (this.showsOtherMonths) {
					cell.className += " othermonth";
					cell.otherMonth = true;
				} else {
					cell.className = "emptycell";
					cell.innerHTML = "&nbsp;";
					cell.disabled = true;
					continue;
				}
			} else {
				cell.otherMonth = false;
				hasdays = true;
			}
			cell.disabled = false;
			cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
			if (dates)
				dates[date.print("%Y%m%d")] = cell;
			if (this.getDateStatus) {
				var status = this.getDateStatus(date, year, month, iday);
				if (this.getDateToolTip) {
					var toolTip = this.getDateToolTip(date, year, month, iday);
					if (toolTip)
						cell.title = toolTip;
				}
				if (status === true) {
					cell.className += " disabled";
					cell.disabled = true;
				} else {
					if (/disabled/i.test(status))
						cell.disabled = true;
					cell.className += " " + status;
				}
			}
			if (!cell.disabled) {
				cell.caldate = new Date(date);
				cell.ttip = "_";
				if (!this.multiple && current_month
				    && iday == mday && this.hiliteToday) {
					cell.className += " selected";
					this.currentDateEl = cell;
				}
				if (date.getFullYear() == TY &&
				    date.getMonth() == TM &&
				    iday == TD) {
					cell.className += " today";
					cell.ttip += Calendar._TT["PART_TODAY"];
				}
				if (weekend.indexOf(wday.toString()) != -1)
					cell.className += cell.otherMonth ? " oweekend" : " weekend";
			}
		}
		if (!(hasdays || this.showsOtherMonths))
			row.className = "emptyrow";
	}
	this.title.innerHTML = Calendar._MN[month] + ", " + year;
	this.onSetTime();
	this.table.style.visibility = "visible";
	this._initMultipleDates();
	// PROFILE
	// this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
};

Calendar.prototype._initMultipleDates = function() {
	if (this.multiple) {
		for (var i in this.multiple) {
			var cell = this.datesCells[i];
			var d = this.multiple[i];
			if (!d)
				continue;
			if (cell)
				cell.className += " selected";
		}
	}
};

Calendar.prototype._toggleMultipleDate = function(date) {
	if (this.multiple) {
		var ds = date.print("%Y%m%d");
		var cell = this.datesCells[ds];
		if (cell) {
			var d = this.multiple[ds];
			if (!d) {
				Calendar.addClass(cell, "selected");
				this.multiple[ds] = date;
			} else {
				Calendar.removeClass(cell, "selected");
				delete this.multiple[ds];
			}
		}
	}
};

Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
	this.getDateToolTip = unaryFunction;
};

/**
 *  Calls _init function above for going to a certain date (but only if the
 *  date is different than the currently selected one).
 */
Calendar.prototype.setDate = function (date) {
	if (!date.equalsTo(this.date)) {
		this._init(this.firstDayOfWeek, date);
	}
};

/**
 *  Refreshes the calendar.  Useful if the "disabledHandler" function is
 *  dynamic, meaning that the list of disabled date can change at runtime.
 *  Just * call this function if you think that the list of disabled dates
 *  should * change.
 */
Calendar.prototype.refresh = function () {
	this._init(this.firstDayOfWeek, this.date);
};

/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
	this._init(firstDayOfWeek, this.date);
	this._displayWeekdays();
};

/**
 *  Allows customization of what dates are enabled.  The "unaryFunction"
 *  parameter must be a function object that receives the date (as a JS Date
 *  object) and returns a boolean value.  If the returned value is true then
 *  the passed date will be marked as disabled.
 */
Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
	this.getDateStatus = unaryFunction;
};

/** Customization of allowed year range for the calendar. */
Calendar.prototype.setRange = function (a, z) {
	this.minYear = a;
	this.maxYear = z;
};

/** Calls the first user handler (selectedHandler). */
Calendar.prototype.callHandler = function () {
	if (this.onSelected) {
		this.onSelected(this, this.date.print(this.dateFormat));
	}
};

/** Calls the second user handler (closeHandler). */
Calendar.prototype.callCloseHandler = function () {
	if (this.onClose) {
		this.onClose(this);
	}
	this.hideShowCovered();
};

/** Removes the calendar object from the DOM tree and destroys it. */
Calendar.prototype.destroy = function () {
	var el = this.element.parentNode;
	el.removeChild(this.element);
	Calendar._C = null;
	window._dynarch_popupCalendar = null;
};

/**
 *  Moves the calendar element to a different section in the DOM tree (changes
 *  its parent).
 */
Calendar.prototype.reparent = function (new_parent) {
	var el = this.element;
	el.parentNode.removeChild(el);
	new_parent.appendChild(el);
};

// This gets called when the user presses a mouse button anywhere in the
// document, if the calendar is shown.  If the click was outside the open
// calendar this function closes it.
Calendar._checkCalendar = function(ev) {
	var calendar = window._dynarch_popupCalendar;
	if (!calendar) {
		return false;
	}
	var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
	for (; el != null && el != calendar.element; el = el.parentNode);
	if (el == null) {
		// calls closeHandler which should hide the calendar.
		window._dynarch_popupCalendar.callCloseHandler();
		return Calendar.stopEvent(ev);
	}
};

/** Shows the calendar. */
Calendar.prototype.show = function () {
	var rows = this.table.getElementsByTagName("tr");
	for (var i = rows.length; i > 0;) {
		var row = rows[--i];
		Calendar.removeClass(row, "rowhilite");
		var cells = row.getElementsByTagName("td");
		for (var j = cells.length; j > 0;) {
			var cell = cells[--j];
			Calendar.removeClass(cell, "hilite");
			Calendar.removeClass(cell, "active");
		}
	}
	this.element.style.display = "block";
	this.hidden = false;
	if (this.isPopup) {
		window._dynarch_popupCalendar = this;
		Calendar.addEvent(document, "keydown", Calendar._keyEvent);
		Calendar.addEvent(document, "keypress", Calendar._keyEvent);
		Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
	}
	this.hideShowCovered();
};

/**
 *  Hides the calendar.  Also removes any "hilite" from the class of any TD
 *  element.
 */
Calendar.prototype.hide = function () {
	if (this.isPopup) {
		Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
		Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
		Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
	}
	this.element.style.display = "none";
	this.hidden = true;
	this.hideShowCovered();
};

/**
 *  Shows the calendar at a given absolute position (beware that, depending on
 *  the calendar element style -- position property -- this might be relative
 *  to the parent's containing rectangle).
 */
Calendar.prototype.showAt = function (x, y) {
	var s = this.element.style;
	s.left = x + "px";
	s.top = y + "px";
	this.show();
};

/** Shows the calendar near a given element. */
Calendar.prototype.showAtElement = function (el, opts) {
	var self = this;
	var p = Calendar.getAbsolutePos(el);
	if (!opts || typeof opts != "string") {
		this.showAt(p.x, p.y + el.offsetHeight);
		return true;
	}
	function fixPosition(box) {
		if (box.x < 0)
			box.x = 0;
		if (box.y < 0)
			box.y = 0;
		var cp = document.createElement("div");
		var s = cp.style;
		s.position = "absolute";
		s.right = s.bottom = s.width = s.height = "0px";
		document.body.appendChild(cp);
		var br = Calendar.getAbsolutePos(cp);
		document.body.removeChild(cp);
		if (Calendar.is_ie) {
			br.y += document.body.scrollTop;
			br.x += document.body.scrollLeft;
		} else {
			br.y += window.scrollY;
			br.x += window.scrollX;
		}
		var tmp = box.x + box.width - br.x;
		if (tmp > 0) box.x -= tmp;
		tmp = box.y + box.height - br.y;
		if (tmp > 0) box.y -= tmp;
	};
	this.element.style.display = "block";
	Calendar.continuation_for_the_fucking_khtml_browser = function() {
		var w = self.element.offsetWidth;
		var h = self.element.offsetHeight;
		self.element.style.display = "none";
		var valign = opts.substr(0, 1);
		var halign = "l";
		if (opts.length > 1) {
			halign = opts.substr(1, 1);
		}
		// vertical alignment
		switch (valign) {
		    case "T": p.y -= h; break;
		    case "B": p.y += el.offsetHeight; break;
		    case "C": p.y += (el.offsetHeight - h) / 2; break;
		    case "t": p.y += el.offsetHeight - h; break;
		    case "b": break; // already there
		}
		// horizontal alignment
		switch (halign) {
		    case "L": p.x -= w; break;
		    case "R": p.x += el.offsetWidth; break;
		    case "C": p.x += (el.offsetWidth - w) / 2; break;
		    case "l": p.x += el.offsetWidth - w; break;
		    case "r": break; // already there
		}
		p.width = w;
		p.height = h + 40;
		self.monthsCombo.style.display = "none";
		fixPosition(p);
		self.showAt(p.x, p.y);
	};
	if (Calendar.is_khtml)
		setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
	else
		Calendar.continuation_for_the_fucking_khtml_browser();
};

/** Customizes the date format. */
Calendar.prototype.setDateFormat = function (str) {
	this.dateFormat = str;
};

/** Customizes the tooltip date format. */
Calendar.prototype.setTtDateFormat = function (str) {
	this.ttDateFormat = str;
};

/**
 *  Tries to identify the date represented in a string.  If successful it also
 *  calls this.setDate which moves the calendar to the given date.
 */
Calendar.prototype.parseDate = function(str, fmt) {
	if (!fmt)
		fmt = this.dateFormat;
	this.setDate(Date.parseDate(str, fmt));
};

Calendar.prototype.hideShowCovered = function () {
	if (!Calendar.is_ie && !Calendar.is_opera)
		return;
	function getVisib(obj){
		var value = obj.style.visibility;
		if (!value) {
			if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
				if (!Calendar.is_khtml)
					value = document.defaultView.
						getComputedStyle(obj, "").getPropertyValue("visibility");
				else
					value = '';
			} else if (obj.currentStyle) { // IE
				value = obj.currentStyle.visibility;
			} else
				value = '';
		}
		return value;
	};

	var tags = new Array("applet", "iframe", "select");
	var el = this.element;

	var p = Calendar.getAbsolutePos(el);
	var EX1 = p.x;
	var EX2 = el.offsetWidth + EX1;
	var EY1 = p.y;
	var EY2 = el.offsetHeight + EY1;

	for (var k = tags.length; k > 0; ) {
		var ar = document.getElementsByTagName(tags[--k]);
		var cc = null;

		for (var i = ar.length; i > 0;) {
			cc = ar[--i];

			p = Calendar.getAbsolutePos(cc);
			var CX1 = p.x;
			var CX2 = cc.offsetWidth + CX1;
			var CY1 = p.y;
			var CY2 = cc.offsetHeight + CY1;

			if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
				if (!cc.__msh_save_visibility) {
					cc.__msh_save_visibility = getVisib(cc);
				}
				cc.style.visibility = cc.__msh_save_visibility;
			} else {
				if (!cc.__msh_save_visibility) {
					cc.__msh_save_visibility = getVisib(cc);
				}
				cc.style.visibility = "hidden";
			}
		}
	}
};

/** Internal function; it displays the bar with the names of the weekday. */
Calendar.prototype._displayWeekdays = function () {
	var fdow = this.firstDayOfWeek;
	var cell = this.firstdayname;
	var weekend = Calendar._TT["WEEKEND"];
	for (var i = 0; i < 7; ++i) {
		cell.className = "day name";
		var realday = (i + fdow) % 7;
		if (i) {
			cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
			cell.navtype = 100;
			cell.calendar = this;
			cell.fdow = realday;
			Calendar._add_evs(cell);
		}
		if (weekend.indexOf(realday.toString()) != -1) {
			Calendar.addClass(cell, "weekend");
		}
		cell.innerHTML = Calendar._SDN[(i + fdow) % 7];
		cell = cell.nextSibling;
	}
};

/** Internal function.  Hides all combo boxes that might be displayed. */
Calendar.prototype._hideCombos = function () {
	this.monthsCombo.style.display = "none";
	this.yearsCombo.style.display = "none";
};

/** Internal function.  Starts dragging the element. */
Calendar.prototype._dragStart = function (ev) {
	if (this.dragging) {
		return;
	}
	this.dragging = true;
	var posX;
	var posY;
	if (Calendar.is_ie) {
		posY = window.event.clientY + document.body.scrollTop;
		posX = window.event.clientX + document.body.scrollLeft;
	} else {
		posY = ev.clientY + window.scrollY;
		posX = ev.clientX + window.scrollX;
	}
	var st = this.element.style;
	this.xOffs = posX - parseInt(st.left);
	this.yOffs = posY - parseInt(st.top);
	with (Calendar) {
		addEvent(document, "mousemove", calDragIt);
		addEvent(document, "mouseup", calDragEnd);
	}
};

// BEGIN: DATE OBJECT PATCHES

/** Adds the number of days array to the Date object. */
Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

/** Constants used for time computations */
Date.SECOND = 1000 /* milliseconds */;
Date.MINUTE = 60 * Date.SECOND;
Date.HOUR   = 60 * Date.MINUTE;
Date.DAY    = 24 * Date.HOUR;
Date.WEEK   =  7 * Date.DAY;

Date.parseDate = function(str, fmt) {
	var today = new Date();
	var y = 0;
	var m = -1;
	var d = 0;
	var a = str.split(/\W+/);
	var b = fmt.match(/%./g);
	var i = 0, j = 0;
	var hr = 0;
	var min = 0;
	for (i = 0; i < a.length; ++i) {
		if (!a[i])
			continue;
		switch (b[i]) {
		    case "%d":
		    case "%e":
			d = parseInt(a[i], 10);
			break;

		    case "%m":
			m = parseInt(a[i], 10) - 1;
			break;

		    case "%Y":
		    case "%y":
			y = parseInt(a[i], 10);
			(y < 100) && (y += (y > 29) ? 1900 : 2000);
			break;

		    case "%b":
		    case "%B":
			for (j = 0; j < 12; ++j) {
				if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
			}
			break;

		    case "%H":
		    case "%I":
		    case "%k":
		    case "%l":
			hr = parseInt(a[i], 10);
			break;

		    case "%P":
		    case "%p":
			if (/pm/i.test(a[i]) && hr < 12)
				hr += 12;
			else if (/am/i.test(a[i]) && hr >= 12)
				hr -= 12;
			break;

		    case "%M":
			min = parseInt(a[i], 10);
			break;
		}
	}
	if (isNaN(y)) y = today.getFullYear();
	if (isNaN(m)) m = today.getMonth();
	if (isNaN(d)) d = today.getDate();
	if (isNaN(hr)) hr = today.getHours();
	if (isNaN(min)) min = today.getMinutes();
	if (y != 0 && m != -1 && d != 0)
		return new Date(y, m, d, hr, min, 0);
	y = 0; m = -1; d = 0;
	for (i = 0; i < a.length; ++i) {
		if (a[i].search(/[a-zA-Z]+/) != -1) {
			var t = -1;
			for (j = 0; j < 12; ++j) {
				if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
			}
			if (t != -1) {
				if (m != -1) {
					d = m+1;
				}
				m = t;
			}
		} else if (parseInt(a[i], 10) <= 12 && m == -1) {
			m = a[i]-1;
		} else if (parseInt(a[i], 10) > 31 && y == 0) {
			y = parseInt(a[i], 10);
			(y < 100) && (y += (y > 29) ? 1900 : 2000);
		} else if (d == 0) {
			d = a[i];
		}
	}
	if (y == 0)
		y = today.getFullYear();
	if (m != -1 && d != 0)
		return new Date(y, m, d, hr, min, 0);
	return today;
};

/** Returns the number of days in the current month */
Date.prototype.getMonthDays = function(month) {
	var year = this.getFullYear();
	if (typeof month == "undefined") {
		month = this.getMonth();
	}
	if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
		return 29;
	} else {
		return Date._MD[month];
	}
};

/** Returns the number of day in the year. */
Date.prototype.getDayOfYear = function() {
	var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
	var time = now - then;
	return Math.floor(time / Date.DAY);
};

/** Returns the number of the week in year, as defined in ISO 8601. */
Date.prototype.getWeekNumber = function() {
	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var DoW = d.getDay();
	d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
	var ms = d.valueOf(); // GMT
	d.setMonth(0);
	d.setDate(4); // Thu in Week 1
	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};

/** Checks date and time equality */
Date.prototype.equalsTo = function(date) {
	return ((this.getFullYear() == date.getFullYear()) &&
		(this.getMonth() == date.getMonth()) &&
		(this.getDate() == date.getDate()) &&
		(this.getHours() == date.getHours()) &&
		(this.getMinutes() == date.getMinutes()));
};

/** Set only the year, month, date parts (keep existing time) */
Date.prototype.setDateOnly = function(date) {
	var tmp = new Date(date);
	this.setDate(1);
	this.setFullYear(tmp.getFullYear());
	this.setMonth(tmp.getMonth());
	this.setDate(tmp.getDate());
};

/** Prints the date in a string according to the given format. */
Date.prototype.print = function (str) {
	var m = this.getMonth();
	var d = this.getDate();
	var y = this.getFullYear();
	var wn = this.getWeekNumber();
	var w = this.getDay();
	var s = {};
	var hr = this.getHours();
	var pm = (hr >= 12);
	var ir = (pm) ? (hr - 12) : hr;
	var dy = this.getDayOfYear();
	if (ir == 0)
		ir = 12;
	var min = this.getMinutes();
	var sec = this.getSeconds();
	s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
	s["%A"] = Calendar._DN[w]; // full weekday name
	s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
	s["%B"] = Calendar._MN[m]; // full month name
	// FIXME: %c : preferred date and time representation for the current locale
	s["%C"] = 1 + Math.floor(y / 100); // the century number
	s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
	s["%e"] = d; // the day of the month (range 1 to 31)
	// FIXME: %D : american date style: %m/%d/%y
	// FIXME: %E, %F, %G, %g, %h (man strftime)
	s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
	s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
	s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
	s["%k"] = hr;		// hour, range 0 to 23 (24h format)
	s["%l"] = ir;		// hour, range 1 to 12 (12h format)
	s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
	s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
	s["%n"] = "\n";		// a newline character
	s["%p"] = pm ? "PM" : "AM";
	s["%P"] = pm ? "pm" : "am";
	// FIXME: %r : the time in am/pm notation %I:%M:%S %p
	// FIXME: %R : the time in 24-hour notation %H:%M
	s["%s"] = Math.floor(this.getTime() / 1000);
	s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
	s["%t"] = "\t";		// a tab character
	// FIXME: %T : the time in 24-hour notation (%H:%M:%S)
	s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
	s["%u"] = w + 1;	// the day of the week (range 1 to 7, 1 = MON)
	s["%w"] = w;		// the day of the week (range 0 to 6, 0 = SUN)
	// FIXME: %x : preferred date representation for the current locale without the time
	// FIXME: %X : preferred time representation for the current locale without the date
	s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
	s["%Y"] = y;		// year with the century
	s["%%"] = "%";		// a literal '%' character

	var re = /%./g;
	if (!Calendar.is_ie5 && !Calendar.is_khtml)
		return str.replace(re, function (par) { return s[par] || par; });

	var a = str.match(re);
	for (var i = 0; i < a.length; i++) {
		var tmp = s[a[i]];
		if (tmp) {
			re = new RegExp(a[i], 'g');
			str = str.replace(re, tmp);
		}
	}

	return str;
};

Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
Date.prototype.setFullYear = function(y) {
	var d = new Date(this);
	d.__msh_oldSetFullYear(y);
	if (d.getMonth() != this.getMonth())
		this.setDate(28);
	this.__msh_oldSetFullYear(y);
};

// END: DATE OBJECT PATCHES


// global object that remembers the calendar
window._dynarch_popupCalendar = null;
/*  Copyright Mihai Bazon, 2002, 2003  |  http://dynarch.com/mishoo/
 * ---------------------------------------------------------------------------
 *
 * The DHTML Calendar
 *
 * Details and latest version at:
 * http://dynarch.com/mishoo/calendar.epl
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 *
 * This file defines helper functions for setting up the calendar.  They are
 * intended to help non-programmers get a working calendar on their site
 * quickly.  This script should not be seen as part of the calendar.  It just
 * shows you what one can do with the calendar, while in the same time
 * providing a quick and simple method for setting it up.  If you need
 * exhaustive customization of the calendar creation process feel free to
 * modify this code to suit your needs (this is recommended and much better
 * than modifying calendar.js itself).
 */

// $Id: calendar-setup.js,v 1.25 2005/03/07 09:51:33 mishoo Exp $

/**
 *  This function "patches" an input field (or other element) to use a calendar
 *  widget for date selection.
 *
 *  The "params" is a single object that can have the following properties:
 *
 *    prop. name   | description
 *  -------------------------------------------------------------------------------------------------
 *   inputField    | the ID of an input field to store the date
 *   displayArea   | the ID of a DIV or other element to show the date
 *   button        | ID of a button or other element that will trigger the calendar
 *   eventName     | event that will trigger the calendar, without the "on" prefix (default: "click")
 *   ifFormat      | date format that will be stored in the input field
 *   daFormat      | the date format that will be used to display the date in displayArea
 *   singleClick   | (true/false) wether the calendar is in single click mode or not (default: true)
 *   firstDay      | numeric: 0 to 6.  "0" means display Sunday first, "1" means display Monday first, etc.
 *   align         | alignment (default: "Br"); if you don't know what's this see the calendar documentation
 *   range         | array with 2 elements.  Default: [1900, 2999] -- the range of years available
 *   weekNumbers   | (true/false) if it's true (default) the calendar will display week numbers
 *   flat          | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID
 *   flatCallback  | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar)
 *   disableFunc   | function that receives a JS Date object and should return true if that date has to be disabled in the calendar
 *   onSelect      | function that gets called when a date is selected.  You don't _have_ to supply this (the default is generally okay)
 *   onClose       | function that gets called when the calendar is closed.  [default]
 *   onUpdate      | function that gets called after the date is updated in the input field.  Receives a reference to the calendar.
 *   date          | the date that the calendar will be initially displayed to
 *   showsTime     | default: false; if true the calendar will include a time selector
 *   timeFormat    | the time format; can be "12" or "24", default is "12"
 *   electric      | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close
 *   step          | configures the step of the years in drop-down boxes; default: 2
 *   position      | configures the calendar absolute position; default: null
 *   cache         | if "true" (but default: "false") it will reuse the same calendar object, where possible
 *   showOthers    | if "true" (but default: "false") it will show days from other months too
 *
 *  None of them is required, they all have default values.  However, if you
 *  pass none of "inputField", "displayArea" or "button" you'll get a warning
 *  saying "nothing to setup".
 */
Calendar.setup = function (params) {
	function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };

	param_default("inputField",     null);
	param_default("displayArea",    null);
	param_default("button",         null);
	param_default("eventName",      "click");
	param_default("ifFormat",       "%d.%m.%Y");
	param_default("daFormat",       "%d.%m.%Y");
	param_default("singleClick",    true);
	param_default("disableFunc",    null);
	param_default("dateStatusFunc", params["disableFunc"]);	// takes precedence if both are defined
	param_default("dateText",       null);
	param_default("firstDay",       1);
	param_default("align",          "Br");
	param_default("range",          [1900, 2999]);
	param_default("weekNumbers",    false);
	param_default("flat",           null);
	param_default("flatCallback",   null);
	param_default("onSelect",       null);
	param_default("onClose",        null);
	param_default("onUpdate",       null);
	param_default("date",           null);
	param_default("showsTime",      false);
	param_default("timeFormat",     "24");
	param_default("electric",       true);
	param_default("step",           2);
	param_default("position",       null);
	param_default("cache",          false);
	param_default("showOthers",     true);
	param_default("multiple",       null);
	param_default("frame",       null);

	var tmp = ["inputField", "displayArea", "button"];
	for (var i in tmp) {
		if (typeof params[tmp[i]] == "string") {
			if(params['frame'] == null){
				params[tmp[i]] = document.getElementById(params[tmp[i]]);
			}else{
				var frame = document.getElementById(params['frame']);
            	params[tmp[i]] = frame.contentDocument.getElementById(params[tmp[i]]);
			}

		}
	}
	if (!(params.flat || params.multiple || params.inputField || params.displayArea || params.button)) {
		alert("Calendar.setup:\n  Nothing to setup (no fields found).  Please check your code");
		return false;
	}

	function onSelect(cal) {
		var p = cal.params;
		var update = (cal.dateClicked || p.electric);
		if (update && p.inputField) {
			p.inputField.value = cal.date.print(p.ifFormat);
			if (typeof p.inputField.onchange == "function")
				p.inputField.onchange();
		}
		if (update && p.displayArea)
			p.displayArea.innerHTML = cal.date.print(p.daFormat);
		if (update && typeof p.onUpdate == "function")
			p.onUpdate(cal);
		if (update && p.flat) {
			if (typeof p.flatCallback == "function")
				p.flatCallback(cal);
		}
		if (update && p.singleClick && cal.dateClicked)
			cal.callCloseHandler();
	};

	if (params.flat != null) {
		if (typeof params.flat == "string")
			params.flat = document.getElementById(params.flat);
		if (!params.flat) {
			alert("Calendar.setup:\n  Flat specified but can't find parent.");
			return false;
		}
		var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect);
		cal.showsOtherMonths = params.showOthers;
		cal.showsTime = params.showsTime;
		cal.time24 = (params.timeFormat == "24");
		cal.params = params;
		cal.weekNumbers = params.weekNumbers;
		cal.setRange(params.range[0], params.range[1]);
		cal.setDateStatusHandler(params.dateStatusFunc);
		cal.getDateText = params.dateText;
		if (params.ifFormat) {
			cal.setDateFormat(params.ifFormat);
		}
		if (params.inputField && typeof params.inputField.value == "string") {
			cal.parseDate(params.inputField.value);
		}
		cal.create(params.flat);
		cal.show();
		return cal;
	}

	var triggerEl = params.button || params.displayArea || params.inputField;
	triggerEl["on" + params.eventName] = function() {
		var dateEl = params.inputField || params.displayArea;
		var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
		var mustCreate = false;
		var cal = window.calendar;
		if (dateEl)
			params.date = Date.parseDate(dateEl.value || dateEl.innerHTML, dateFmt);
		if (!(cal && params.cache)) {
			window.calendar = cal = new Calendar(params.firstDay,
							     params.date,
							     params.onSelect || onSelect,
							     params.onClose || function(cal) { cal.hide(); });
			cal.showsTime = params.showsTime;
			cal.time24 = (params.timeFormat == "24");
			cal.weekNumbers = params.weekNumbers;
			mustCreate = true;
		} else {
			if (params.date)
				cal.setDate(params.date);
			cal.hide();
		}
		if (params.multiple) {
			cal.multiple = {};
			for (var i = params.multiple.length; --i >= 0;) {
				var d = params.multiple[i];
				var ds = d.print("%Y%m%d");
				cal.multiple[ds] = d;
			}
		}
		cal.showsOtherMonths = params.showOthers;
		cal.yearStep = params.step;
		cal.setRange(params.range[0], params.range[1]);
		cal.params = params;
		cal.setDateStatusHandler(params.dateStatusFunc);
		cal.getDateText = params.dateText;
		cal.setDateFormat(dateFmt);
		if (mustCreate)
			cal.create();
		cal.refresh();
		if (!params.position)
			cal.showAtElement(params.button || params.displayArea || params.inputField, params.align);
		else
			cal.showAt(params.position[0], params.position[1]);
		return false;
	};

	return cal;
};
