/*
 * jQuery Form Plugin
 * version: 2.25 (08-APR-2009)
 * @requires jQuery v1.2.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function($) {

/**
 * ajaxSubmit() provides a mechanism for immediately submitting
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
	// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
	if (!this.length) {
		log('ajaxSubmit: skipping submit process - no element selected');
		return this;
	}

	if (typeof options == 'function')
		options = { success: options };

	// clean url (don't include hash vaue)
	var url = this.attr('action') || window.location.href;
	url = (url.match(/^([^#]+)/)||[])[1];
	url = url || '';

	options = $.extend({
		url:  url,
		type: this.attr('method') || 'GET'
	}, options || {});

	// hook for manipulating the form data before it is extracted;
	// convenient for use with rich editors like tinyMCE or FCKEditor
	var veto = {};
	this.trigger('form-pre-serialize', [this, options, veto]);
	if (veto.veto) {
		log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
		return this;
	}

	// provide opportunity to alter form data before it is serialized
	if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
		log('ajaxSubmit: submit aborted via beforeSerialize callback');
		return this;
	}

	var a = this.formToArray(options.semantic);
	if (options.data) {
		options.extraData = options.data;
		for (var n in options.data) {
		  if(options.data[n] instanceof Array) {
			for (var k in options.data[n])
			  a.push( { name: n, value: options.data[n][k] } );
		  }
		  else
			 a.push( { name: n, value: options.data[n] } );
		}
	}

	// give pre-submit callback an opportunity to abort the submit
	if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
		log('ajaxSubmit: submit aborted via beforeSubmit callback');
		return this;
	}

	// fire vetoable 'validate' event
	this.trigger('form-submit-validate', [a, this, options, veto]);
	if (veto.veto) {
		log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
		return this;
	}

	var q = $.param(a);

	if (options.type.toUpperCase() == 'GET') {
		options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
		options.data = null;  // data is null for 'get'
	}
	else
		options.data = q; // data is the query string for 'post'

	var $form = this, callbacks = [];
	if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
	if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

	// perform a load on the target only if dataType is not provided
	if (!options.dataType && options.target) {
		var oldSuccess = options.success || function(){};
		callbacks.push(function(data) {
			$(options.target).html(data).each(oldSuccess, arguments);
		});
	}
	else if (options.success)
		callbacks.push(options.success);

	options.success = function(data, status) {
		for (var i=0, max=callbacks.length; i < max; i++)
			callbacks[i].apply(options, [data, status, $form]);
	};

	// are there files to upload?
	var files = [];
	try{files=$('input:file', this).fieldValue();}catch(Ex){}
	var found = false;
	for (var j=0; j < files.length; j++)
		if (files[j])
			found = true;

	// options.iframe allows user to force iframe mode
   if (options.iframe || found) {
	   // hack to fix Safari hang (thanks to Tim Molendijk for this)
	   // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
	   if (options.closeKeepAlive)
		   $.get(options.closeKeepAlive, fileUpload);
	   else
		   fileUpload();
	   }
   else
	   $.ajax(options);

	// fire 'notify' event
	this.trigger('form-submit-notify', [this, options]);
	return this;


	// private function for handling file uploads (hat tip to YAHOO!)
	function fileUpload() {
		var form = $form[0];

		if ($(':input[name=submit]', form).length) {
			alert('Error: Form elements must not be named "submit".');
			return;
		}

		var opts = $.extend({}, $.ajaxSettings, options);
		opts['dataType'] = 'html';
		var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

		var id = 'jqFormIO' + (new Date().getTime());
		var $io = $('<iframe id="' + id + '" name="' + id + '" src="about:blank" />');
		var io = $io[0];

		$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

		var xhr = { // mock object
			aborted: 0,
			responseText: null,
			responseXML: null,
			status: 0,
			statusText: 'n/a',
			getAllResponseHeaders: function() {},
			getResponseHeader: function() {},
			setRequestHeader: function() {},
			abort: function() {
				this.aborted = 1;
				$io.attr('src','about:blank'); // abort op in progress
			}
		};

		var g = opts.global;
		// trigger ajax global events so that activity/block indicators work like normal
		if (g && ! $.active++) $.event.trigger("ajaxStart");
		if (g) $.event.trigger("ajaxSend", [xhr, opts]);

		if (s.beforeSend && s.beforeSend(xhr, s) === false) {
			s.global && $.active--;
			return;
		}
		if (xhr.aborted)
			return;

		var cbInvoked = 0;
		var timedOut = 0;

		//add ___iframe to form post to ensure server side knows it's iframe fileupload'
		options.extraData['___iframe'] = 1;

		// add submitting element to data if we know it
		var sub = form.clk;
		if (sub) {
			var n = sub.name;
			if (n && !sub.disabled) {
				options.extraData = options.extraData || {};
				options.extraData[n] = sub.value;
				if (sub.type == "image") {
					options.extraData[name+'.x'] = form.clk_x;
					options.extraData[name+'.y'] = form.clk_y;
				}
			}
		}

		// take a breath so that pending repaints get some cpu time before the upload starts
		setTimeout(function() {
			// make sure form attrs are set
			var t = $form.attr('target'), a = $form.attr('action');

			// update form attrs in IE friendly way
			form.setAttribute('target',id);
			if (form.getAttribute('method') != 'POST')
				form.setAttribute('method', 'POST');
			if (form.getAttribute('action') != opts.url)
				form.setAttribute('action', opts.url);

			// ie borks in some cases when setting encoding
			if (! options.skipEncodingOverride) {
				$form.attr({
					encoding: 'multipart/form-data',
					enctype:  'multipart/form-data'
				});
			}

			// support timout
			if (opts.timeout)
				setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

			// add "extra" data to form if provided in options
			var extraInputs = [];
			try {
				if (options.extraData)
					for (var n in options.extraData)
						extraInputs.push(
							$('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
								.appendTo(form)[0]);

				// add iframe to doc and submit the form
				$io.appendTo('body');
				io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
				form.submit();
			}
			finally {
				// reset attrs and remove "extra" input elements
				form.setAttribute('action',a);
				t ? form.setAttribute('target', t) : $form.removeAttr('target');
				$(extraInputs).remove();
			}
		}, 10);

		var nullCheckFlag = 0;

		function cb() {
			if (cbInvoked++) return;

			io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);

			var ok = true;
			try {
				if (timedOut) throw 'timeout';
				// extract the server response from the iframe
				var data, doc;

				doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;

				if ((doc.body == null || doc.body.innerHTML == '') && !nullCheckFlag) {
					// in some browsers (cough, Opera 9.2.x) the iframe DOM is not always traversable when
					// the onload callback fires, so we give them a 2nd chance
					nullCheckFlag = 1;
					cbInvoked--;
					setTimeout(cb, 100);
					return;
				}

				xhr.responseText = doc.body ? doc.body.innerHTML : null;
				xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
				xhr.getResponseHeader = function(header){
					var headers = {'content-type': opts.dataType == 'json' ? 'text/x-json' : opts.dataType};
					return headers[header];
				};

				if (opts.dataType == 'json' || opts.dataType == 'script') {
					var ta = doc.getElementsByTagName('textarea')[0];
					xhr.responseText = ta ? ta.value : xhr.responseText;
				}
				else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
					xhr.responseXML = toXml(xhr.responseText);
				}
				data = $.httpData(xhr, opts.dataType);
			}
			catch(e){
				ok = false;
				$.handleError(opts, xhr, 'error', e);
				throw e;
			}

			// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
			if (ok) {
				opts.success(data, 'success');
				if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
			}
			if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
			if (g && ! --$.active) $.event.trigger("ajaxStop");
			if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

			// clean up
			setTimeout(function() {
				$io.remove();
				xhr.responseXML = null;
			}, 100);
		};

		function toXml(s, doc) {
			if (window.ActiveXObject) {
				doc = new ActiveXObject('Microsoft.XMLDOM');
				doc.async = 'false';
				doc.loadXML(s);
			}
			else
				doc = (new DOMParser()).parseFromString(s, 'text/xml');
			return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
		};
	};
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *	is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *	used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */
$.fn.ajaxForm = function(options) {
	return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
		$(this).ajaxSubmit(options);
		return false;
	}).each(function() {
		// store options in hash
		$(":submit,input:image", this).bind('click.form-plugin',function(e) {
			var form = this.form;
			form.clk = this;
			if (this.type == 'image') {
				if (e.offsetX != undefined) {
					form.clk_x = e.offsetX;
					form.clk_y = e.offsetY;
				} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
					var offset = $(this).offset();
					form.clk_x = e.pageX - offset.left;
					form.clk_y = e.pageY - offset.top;
				} else {
					form.clk_x = e.pageX - this.offsetLeft;
					form.clk_y = e.pageY - this.offsetTop;
				}
			}
			// clear form vars
			setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10);
		});
	});
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
	this.unbind('submit.form-plugin');
	return this.each(function() {
		try{
		$(":submit,input:image", this).unbind('click.form-plugin');
		}catch(Ex){}
	});
};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
	var a = [];
	if (this.length == 0) return a;

	var form = this[0];
	var els = semantic ? form.getElementsByTagName('*') : form.elements;
	if (!els) return a;
	for(var i=0, max=els.length; i < max; i++) {
		var el = els[i];
		var n = el.name;
		if (!n) continue;

		if (semantic && form.clk && el.type == "image") {
			// handle image inputs on the fly when semantic == true
			if(!el.disabled && form.clk == el)
				a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
			continue;
		}

		var v = $.fieldValue(el, true);
		if (v && v.constructor == Array) {
			for(var j=0, jmax=v.length; j < jmax; j++)
				a.push({name: n, value: v[j]});
		}
		else if (v !== null && typeof v != 'undefined')
			a.push({name: n, value: v});
	}

	if (!semantic && form.clk) {
		// input type=='image' are not found in elements array! handle them here
		var inputs = form.getElementsByTagName("input");
		for(var i=0, max=inputs.length; i < max; i++) {
			var input = inputs[i];
			var n = input.name;
			if(n && !input.disabled && input.type == "image" && form.clk == input)
				a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
		}
	}
	return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
	//hand off to jQuery.param for proper encoding
	return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
	var a = [];
	this.each(function() {
		var n = this.name;
		if (!n) return;
		var v = $.fieldValue(this, successful);
		if (v && v.constructor == Array) {
			for (var i=0,max=v.length; i < max; i++)
				a.push({name: n, value: v[i]});
		}
		else if (v !== null && typeof v != 'undefined')
			a.push({name: this.name, value: v});
	});
	//hand off to jQuery.param for proper encoding
	return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *	   array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
	for (var val=[], i=0, max=this.length; i < max; i++) {
		var el = this[i];
		var v = $.fieldValue(el, successful);
		if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
			continue;
		v.constructor == Array ? $.merge(val, v) : val.push(v);
	}
	return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
	var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
	if (typeof successful == 'undefined') successful = true;

	if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
		(t == 'checkbox' || t == 'radio') && !el.checked ||
		(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
		tag == 'select' && el.selectedIndex == -1))
			return null;

	if (tag == 'select') {
		var index = el.selectedIndex;
		if (index < 0) return null;
		var a = [], ops = el.options;
		var one = (t == 'select-one');
		var max = (one ? index+1 : ops.length);
		for(var i=(one ? index : 0); i < max; i++) {
			var op = ops[i];
			if (op.selected) {
				var v = op.value;
				if (!v) // extra pain for IE...
					v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
				if (one) return v;
				a.push(v);
			}
		}
		return a;
	}
	return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
	return this.each(function() {
		$('input,select,textarea', this).clearFields();
	});
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
	return this.each(function() {
		var t = this.type, tag = this.tagName.toLowerCase();
		if (t == 'text' || t == 'password' || tag == 'textarea')
			this.value = '';
		else if (t == 'checkbox' || t == 'radio')
			this.checked = false;
		else if (tag == 'select')
			this.selectedIndex = -1;
	});
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
	return this.each(function() {
		// guard against an input with the name of 'reset'
		// note that IE reports the reset function as an 'object'
		if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
			this.reset();
	});
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) {
	if (b == undefined) b = true;
	return this.each(function() {
		this.disabled = !b;
	});
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.selected = function(select) {
	if (select == undefined) select = true;
	return this.each(function() {
		var t = this.type;
		if (t == 'checkbox' || t == 'radio')
			this.checked = select;
		else if (this.tagName.toLowerCase() == 'option') {
			var $sel = $(this).parent('select');
			if (select && $sel[0] && $sel[0].type == 'select-one') {
				// deselect all other options
				$sel.find('option').selected(false);
			}
			this.selected = select;
		}
	});
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
	if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
		window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
}

})(jQuery);

//selectboxes modification block
/*
 *
 * Copyright (c) 2006-2009 Sam Collett (http://www.texotela.co.uk)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Version 2.2.4
 * Demo: http://www.texotela.co.uk/code/jquery/select/
 *
 * $LastChangedDate$
 * $Rev$
 *
 */

;(function($) {

/**
 * Adds (single/multiple) options to a select box (or series of select boxes)
 *
 * @name	 addOption
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 jQuery
 * @example  $("#myselect").addOption("Value", "Text"); // add single value (will be selected)
 * @example  $("#myselect").addOption("Value 2", "Text 2", false); // add single value (won't be selected)
 * @example  $("#myselect").addOption({"foo":"bar","bar":"baz"}, false); // add multiple values, but don't select
 *
 */
$.fn.addOption = function()
{
	var add = function(el, v, t, sO)
	{
		var option = document.createElement("option");
		option.value = v, option.text = t;
		// get options
		var o = el.options;
		// get number of options
		var oL = o.length;
		if(!el.cache)
		{
			el.cache = {};
			// loop through existing options, adding to cache
			for(var i = 0; i < oL; i++)
			{
				el.cache[o[i].value] = i;
			}
		}
		// add to cache if it isn't already
		if(typeof el.cache[v] == "undefined") el.cache[v] = oL;
		el.options[el.cache[v]] = option;
		if(sO)
		{
			option.selected = true;
		}
	};

	var a = arguments;
	if(a.length == 0) return this;
	// select option when added? default is true
	var sO = true;
	// multiple items
	var m = false;
	// other variables
	var items, v, t;
	if(typeof(a[0]) == "object")
	{
		m = true;
		items = a[0];
	}
	if(a.length >= 2)
	{
		if(typeof(a[1]) == "boolean") sO = a[1];
		else if(typeof(a[2]) == "boolean") sO = a[2];
		if(!m)
		{
			v = a[0];
			t = a[1];
		}
	}
	this.each(
		function()
		{
			if(this.nodeName.toLowerCase() != "select") return;
			if(m)
			{
				for(var item in items)
				{
					add(this, item, items[item], sO);
				}
			}
			else
			{
				add(this, v, t, sO);
			}
		}
	);
	return this;
};

/**
 * Add options via ajax
 *
 * @name	 ajaxAddOption
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 jQuery
 * @param	String url	  Page to get options from (must be valid JSON)
 * @param	Object params   (optional) Any parameters to send with the request
 * @param	Boolean select  (optional) Select the added options, default true
 * @param	Function fn	 (optional) Call this function with the select object as param after completion
 * @param	Array args	  (optional) Array with params to pass to the function afterwards
 * @example  $("#myselect").ajaxAddOption("myoptions.php");
 * @example  $("#myselect").ajaxAddOption("myoptions.php", {"code" : "007"});
 * @example  $("#myselect").ajaxAddOption("myoptions.php", {"code" : "007"}, false, sortoptions, [{"dir": "desc"}]);
 *
 */
$.fn.ajaxAddOption = function(url, params, select, fn, args)
{
	if(typeof(url) != "string") return this;
	if(typeof(params) != "object") params = {};
	if(typeof(select) != "boolean") select = true;
	this.each(
		function()
		{
			var el = this;
			$.getJSON(url,
				params,
				function(r)
				{
					$(el).addOption(r, select);
					if(typeof fn == "function")
					{
						if(typeof args == "object")
						{
							fn.apply(el, args);
						}
						else
						{
							fn.call(el);
						}
					}
				}
			);
		}
	);
	return this;
};

/**
 * Removes an option (by value or index) from a select box (or series of select boxes)
 *
 * @name	 removeOption
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 jQuery
 * @param	String|RegExp|Number what  Option to remove
 * @param	Boolean selectedOnly	   (optional) Remove only if it has been selected (default false)
 * @example  $("#myselect").removeOption("Value"); // remove by value
 * @example  $("#myselect").removeOption(/^val/i); // remove options with a value starting with 'val'
 * @example  $("#myselect").removeOption(/./); // remove all options
 * @example  $("#myselect").removeOption(/./, true); // remove all options that have been selected
 * @example  $("#myselect").removeOption(0); // remove by index
 * @example  $("#myselect").removeOption(["myselect_1","myselect_2"]); // values contained in passed array
 *
 */
$.fn.removeOption = function()
{
	var a = arguments;
	if(a.length == 0) return this;
	var ta = typeof(a[0]);
	var v, index;
	// has to be a string or regular expression (object in IE, function in Firefox)
	if(ta == "string" || ta == "object" || ta == "function" )
	{
		v = a[0];
		// if an array, remove items
		if(v.constructor == Array)
		{
			var l = v.length;
			for(var i = 0; i<l; i++)
			{
				this.removeOption(v[i], a[1]);
			}
			return this;
		}
	}
	else if(ta == "number") index = a[0];
	else return this;
	this.each(
		function()
		{
			if(this.nodeName.toLowerCase() != "select") return;
			// clear cache
			if(this.cache) this.cache = null;
			// does the option need to be removed?
			var remove = false;
			// get options
			var o = this.options;
			if(!!v)
			{
				// get number of options
				var oL = o.length;
				for(var i=oL-1; i>=0; i--)
				{
					if(v.constructor == RegExp)
					{
						if(o[i].value.match(v))
						{
							remove = true;
						}
					}
					else if(o[i].value == v)
					{
						remove = true;
					}
					// if the option is only to be removed if selected
					if(remove && a[1] === true) remove = o[i].selected;
					if(remove)
					{
						o[i] = null;
					}
					remove = false;
				}
			}
			else
			{
				// only remove if selected?
				if(a[1] === true)
				{
					remove = o[index].selected;
				}
				else
				{
					remove = true;
				}
				if(remove)
				{
					this.remove(index);
				}
			}
		}
	);
	return this;
};

/**
 * Sort options (ascending or descending) in a select box (or series of select boxes)
 *
 * @name	 sortOptions
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 jQuery
 * @param	Boolean ascending   (optional) Sort ascending (true/undefined), or descending (false)
 * @example  // ascending
 * $("#myselect").sortOptions(); // or $("#myselect").sortOptions(true);
 * @example  // descending
 * $("#myselect").sortOptions(false);
 *
 */
$.fn.sortOptions = function(ascending)
{
	// get selected values first
	var sel = $(this).selectedValues();
	var a = typeof(ascending) == "undefined" ? true : !!ascending;
	this.each(
		function()
		{
			if(this.nodeName.toLowerCase() != "select") return;
			// get options
			var o = this.options;
			// get number of options
			var oL = o.length;
			// create an array for sorting
			var sA = [];
			// loop through options, adding to sort array
			for(var i = 0; i<oL; i++)
			{
				sA[i] = {
					v: o[i].value,
					t: o[i].text
				}
			}
			// sort items in array
			sA.sort(
				function(o1, o2)
				{
					// option text is made lowercase for case insensitive sorting
					o1t = o1.t.toLowerCase(), o2t = o2.t.toLowerCase();
					// if options are the same, no sorting is needed
					if(o1t == o2t) return 0;
					if(a)
					{
						return o1t < o2t ? -1 : 1;
					}
					else
					{
						return o1t > o2t ? -1 : 1;
					}
				}
			);
			// change the options to match the sort array
			for(var i = 0; i<oL; i++)
			{
				o[i].text = sA[i].t;
				o[i].value = sA[i].v;
			}
		}
	).selectOptions(sel, true); // select values, clearing existing ones
	return this;
};
/**
 * Selects an option by value
 *
 * @name	 selectOptions
 * @author   Mathias Bank (http://www.mathias-bank.de), original function
 * @author   Sam Collett (http://www.texotela.co.uk), addition of regular expression matching
 * @type	 jQuery
 * @param	String|RegExp|Array value  Which options should be selected
 * can be a string or regular expression, or an array of strings / regular expressions
 * @param	Boolean clear  Clear existing selected options, default false
 * @example  $("#myselect").selectOptions("val1"); // with the value 'val1'
 * @example  $("#myselect").selectOptions(["val1","val2","val3"]); // with the values 'val1' 'val2' 'val3'
 * @example  $("#myselect").selectOptions(/^val/i); // with the value starting with 'val', case insensitive
 *
 */
$.fn.selectOptions = function(value, clear)
{
	var v = value;
	var vT = typeof(value);
	// handle arrays
	if(vT == "object" && v.constructor == Array)
	{
		var $this = this;
		$.each(v, function()
			{
	  				$this.selectOptions(this, clear);
				}
		);
	};
	var c = clear || false;
	// has to be a string or regular expression (object in IE, function in Firefox)
	if(vT != "string" && vT != "function" && vT != "object") return this;
	this.each(
		function()
		{
			if(this.nodeName.toLowerCase() != "select") return this;
			// get options
			var o = this.options;
			// get number of options
			var oL = o.length;
			for(var i = 0; i<oL; i++)
			{
				if(v.constructor == RegExp)
				{
					if(o[i].value.match(v))
					{
						o[i].selected = true;
					}
					else if(c)
					{
						o[i].selected = false;
					}
				}
				else
				{
					if(o[i].value == v)
					{
						o[i].selected = true;
					}
					else if(c)
					{
						o[i].selected = false;
					}
				}
			}
		}
	);
	return this;
};

/**
 * Copy options to another select
 *
 * @name	 copyOptions
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 jQuery
 * @param	String to  Element to copy to
 * @param	String which  (optional) Specifies which options should be copied - 'all' or 'selected'. Default is 'selected'
 * @example  $("#myselect").copyOptions("#myselect2"); // copy selected options from 'myselect' to 'myselect2'
 * @example  $("#myselect").copyOptions("#myselect2","selected"); // same as above
 * @example  $("#myselect").copyOptions("#myselect2","all"); // copy all options from 'myselect' to 'myselect2'
 *
 */
$.fn.copyOptions = function(to, which)
{
	var w = which || "selected";
	if($(to).size() == 0) return this;
	this.each(
		function()
		{
			if(this.nodeName.toLowerCase() != "select") return this;
			// get options
			var o = this.options;
			// get number of options
			var oL = o.length;
			for(var i = 0; i<oL; i++)
			{
				if(w == "all" || (w == "selected" && o[i].selected))
				{
					$(to).addOption(o[i].value, o[i].text);
				}
			}
		}
	);
	return this;
};

/**
 * Checks if a select box has an option with the supplied value
 *
 * @name	 containsOption
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 Boolean|jQuery
 * @param	String|RegExp value  Which value to check for. Can be a string or regular expression
 * @param	Function fn		  (optional) Function to apply if an option with the given value is found.
 * Use this if you don't want to break the chaining
 * @example  if($("#myselect").containsOption("val1")) alert("Has an option with the value 'val1'");
 * @example  if($("#myselect").containsOption(/^val/i)) alert("Has an option with the value starting with 'val'");
 * @example  $("#myselect").containsOption("val1", copyoption).doSomethingElseWithSelect(); // calls copyoption (user defined function) for any options found, chain is continued
 *
 */
$.fn.containsOption = function(value, fn)
{
	var found = false;
	var v = value;
	var vT = typeof(v);
	var fT = typeof(fn);
	// has to be a string or regular expression (object in IE, function in Firefox)
	if(vT != "string" && vT != "function" && vT != "object") return fT == "function" ? this: found;
	this.each(
		function()
		{
			if(this.nodeName.toLowerCase() != "select") return this;
			// option already found
			if(found && fT != "function") return false;
			// get options
			var o = this.options;
			// get number of options
			var oL = o.length;
			for(var i = 0; i<oL; i++)
			{
				if(v.constructor == RegExp)
				{
					if (o[i].value.match(v))
					{
						found = true;
						if(fT == "function") fn.call(o[i], i);
					}
				}
				else
				{
					if (o[i].value == v)
					{
						found = true;
						if(fT == "function") fn.call(o[i], i);
					}
				}
			}
		}
	);
	return fT == "function" ? this : found;
};

/**
 * Returns values which have been selected
 *
 * @name	 selectedValues
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 Array
 * @example  $("#myselect").selectedValues();
 *
 */
$.fn.selectedValues = function()
{
	var v = [];
	this.selectedOptions().each(
		function()
		{
			v[v.length] = this.value;
		}
	);
	return v;
};

/**
 * Returns text which has been selected
 *
 * @name	 selectedTexts
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 Array
 * @example  $("#myselect").selectedTexts();
 *
 */
$.fn.selectedTexts = function()
{
	var t = [];
	this.selectedOptions().each(
		function()
		{
			t[t.length] = this.text;
		}
	);
	return t;
};

/**
 * Returns options which have been selected
 *
 * @name	 selectedOptions
 * @author   Sam Collett (http://www.texotela.co.uk)
 * @type	 jQuery
 * @example  $("#myselect").selectedOptions();
 *
 */
$.fn.selectedOptions = function()
{
	return this.find("option:selected");
};

})(jQuery);
function simpleAjaxSubmit(form, addonopts) {
	if(!form) return;
	var formid = form.id;
	var opts = {
						success: function(data) {try{processFormReturn(data);}catch(Ex){alert(Ex);}bindAjaxForms();},
						dataType: 'json',
						data: {'___ajax': formid},
						global: '#'+formid+' img.ajaxloader'
	};
	for(var i in addonopts) opts['data'][i] = addonopts[i];
	$(form).ajaxSubmit(opts);
}
//additional code
function bindAjaxForms() {
	$('form.ajaxform').each(function() {
		var formid = this.id;
		$(this).ajaxForm({
			success: function(data) {try{processFormReturn(data);}catch(Ex){alert(Ex);}bindAjaxForms();},
			dataType: 'json',
			data: {'___ajax': formid},
			global: '#'+formid+' img.ajaxloader'
		});
	});
	bindAjaxLoaders();
	bindSerializeEvents();
	$('textarea.autogrow').autoExpand();
	$('textarea').forceMaxlength();
}
function evalScriptTags(elem) {
	try{
		var arr = elem.getElementsByTagName('SCRIPT');
		for(var i = 0; i < arr.length; i++) {
			try{eval(arr[i].innerHTML);}catch(Ex){}
		}
	}catch(Ex){}
}
function tryInsertForm(jsonform, insertjson) {
	var marker, form;
	try{
	if(insertjson['after'][jsonform.formid]) {
		marker = $('#'+insertjson['after'][jsonform.formid]);
		if(insertjson.levels && insertjson.levels[jsonform.formid]) {
			for(var i = 0; i < insertjson.levels[jsonform.formid]; i++) marker = marker.parent();
		}
		if(marker.length == 1) {
			form = $(jsonform.formhtml);
			form.insertAfter(marker);
			evalScriptTags(form[0]);
		}
	}
	}catch(Ex){}
	try{
	if(insertjson['before'][jsonform.formid]) {
		marker = $('#'+insertjson['before'][jsonform.formid]);
		if(marker.length == 1) {
			form = $(jsonform.formhtml);
			form.insertBefore(marker);
			evalScriptTags(form[0]);
		}
	}
	}catch(Ex){}
	try{
	if(insertjson['into'][jsonform.formid]) {
		marker = $('#'+insertjson['into'][jsonform.formid]);
		if(marker.length == 1) {
			marker.html(jsonform.formhtml);
			evalScriptTags(document.getElementById(jsonform.formid));
		}
	}
	}catch(Ex){}
}
function processFormReturn(json) {
	if(typeof json==="string") return;
	if(json.addondata) {
		$.each(json.addondata, function(i, val) {$('#'+i).html(val);evalScriptTags(document.getElementById(i));});
	}
	if(json.forms) {
		for(var i = 0; i < json.forms.length; i++) {
			if(!setFormHtml(json.forms[i].formid, json.forms[i].formhtml)) {
				try{
					if(json.insertdata)
						tryInsertForm(json.forms[i], json.insertdata);
				}catch(Ex) {}
			}
		}
	}
	if(json.eval) {
		eval(json.eval);
	}
	if(json.openwindow) {
		window.open(json.openwindow.href, json.openwindow.target, json.openwindow.options);
	}
	if(json.newlocation) {
		document.location.href = json.newlocation;
		return;
	}
}
$(document).ready(function() {
	bindAjaxForms();
});
function setFormHtml(formid, formhtml) {
	var ret = false;
	try{
		document.getElementById(formid).parentNode.innerHTML = formhtml;
		ret = true;
		evalScriptTags(document.getElementById(formid));
	}catch(Ex){}
	return ret;
}
function bindAjaxLoaders() {
	$('img.ajaxloader').each(function(){
		this.style.visibility = 'visible';
		this.style.zIndex = 10000;
		$(this).hide(0);
		$(this).ajaxSend(function(e, xhr, opts){
			try{
			var formid = this.id.replace('ajaximage_', '');
			if(opts.extraData['___ajax'] == formid)
			$(this).show(0);
			}catch(ex){}
		});
		$(this).ajaxStop(function(e){
			$(this).fadeOut(1000);
		});
	});
}
function bindSerializeEvents() {
	$('form').unbind('form-pre-serialize', handleFckSerializing)
	$('form').bind('form-pre-serialize', handleFckSerializing);
}
function handleFckSerializing(event, $form, formOptions, veto) {
	if(typeof(FCKeditorAPI) == "object") {
		for(var fckname in FCKeditorAPI.Instances ) {
			FCKeditorAPI.GetInstance(fckname).UpdateLinkedField();
			FCKeditorAPI.GetInstance(fckname).Events.FireEvent( 'OnAfterLinkedFieldUpdate' );
		}
	}
}
$(document).ready(function() {
	setInterval(formChecker, 3000);
	formChecker(true);
});
function formChecker(first) {
	try{
		if(!showNotifier && !hideNotifier && !showFormNotifier && !hideFormNotifier) return;
	}catch(Ex) {
		return;
	}
	var changedCount = 0;
	var changedForms = [];
	var notChangedForms = [];
	$('form.notifychanges').each(function() {
		if(!first)handleFckSerializing();
		var params = $(this).formSerialize();
		if(this.previousContents && this.previousContents != params){
			changedCount++;
			changedForms[changedForms.length] = this;
			this.hasChanged = true;
		} else {
			notChangedForms[notChangedForms.length] = this;
			this.hasChanged = false;
		}
		if(!this.previousContents) this.previousContents = params;
	});
	for(var i = 0; i < changedForms.length; i++) {
		try{showFormNotifier(changedForms[i]);}catch(e){}
		$(changedForms[i]).css({'border':'1px solid red'});
	}
	for(i = 0; i < notChangedForms.length; i++) {
		try{hideFormNotifier(notChangedForms[i]);}catch(e){}
		$(notChangedForms[i]).css({'border':'none'});
	}
	if(changedCount > 0) {
		try{showNotifier();}catch(e){}
	} else {
		try{hideNotifier();}catch(e){}
	}
}

function bindSelect(changer_id, data, watched_id) {
	$('#'+watched_id).change(function(e) {
		var ch = $('#'+changer_id);
		ch.removeOption(/./);
		var opts = data[$(this).val()];
		if(!opts) return;
		ch.addOption(opts, false);
	});
}

function switchForms(id1, id2) {
	//get parent spans
	var f1 = $('#'+id1).parent();
	var f2 = $('#'+id2).parent();
	if(!f1.length || !f2.length) return;
	var placeholder1 = $('<span></span>');
	var placeholder2 = $('<span></span>');
	placeholder1.insertBefore(f1);
	placeholder2.insertBefore(f2);
	f1.insertAfter(placeholder2);
	f2.insertAfter(placeholder1);
	placeholder1.remove();
	placeholder2.remove();
	try{bindAjaxForms();}catch(Ex){}
}

function delForm(id, parentlevel) {
	if(!parentlevel) parentlevel = 0;
	//remove with parent span
	var span = $('#'+id).parent();
	for(var i = 0; i < parentlevel; i++) span = span.parent();
	span.remove();
}

/*
* Automatic Expanding Text Area (v0.1)
* by Jonathan Chao
* June 18, 2009
*
* Inspiration comes from Jason Frame's implementation located at
*   http://github.com/jaz303/jquery-grab-bag/blob/master/javascripts/jquery.autogrow-textarea.js
*
* NOTE: This script requires jQuery to work. Developed with jQuery v1.3.2
*/

(function($) {
	$.fn.autoExpand = function(o) {
		var defaults = {
			maxHeight: 400,
			lineHeight: 16
		};
		var options = $.extend(defaults, o);

		return $(this).each(function() {
			try{if(this.autoExpanding) return;}catch(Ex){}
			this.autoExpanding = true;
			var $textarea   = $(this),
				interval	= null,
				minHeight   = Math.max($textarea.height(), 0),
				lineHeight  = Math.max(($.browser.msie) ? options.lineHeight : parseInt($textarea.css("line-height"), 10), 5);
			var div = $('<div></div>').css({
				display:	"none",
				fontSize:   $textarea.css("font-size"),
				fontFamily: $textarea.css("font-family"),
				minHeight:  Math.max($textarea.height() - lineHeight, 0),
				lineHeight: $textarea.css("line-height"),
				position:   'absolute',
				'top':	  0,
				left:	   -9999,
				width:	  $textarea.width() - parseInt($textarea.css("padding-left")) - parseInt($textarea.css("padding-right"))
			}).appendTo(document.body);

			if ($.browser.msie) {
				div.css({ wordWrap: "break-word" });
			}
			else {
				div.css({ whiteSpace: "-moz-pre-wrap", whiteSpace: "pre-wrap" });
			}

			$textarea.css({ overflow: "hidden" });
			$textarea.bind('focus', function() { interval = window.setInterval(checkSize, 10); });
			$textarea.bind('blur', function() { window.clearInterval(interval); });

			var checkSize = function() {
			var repeat = function (str, num) { return new Array(num + 1).join(str); }
				div.html(($textarea.val()+' ').replace(/\n/g, '<br />').replace(/ {2,}/g, function(n) { return repeat("&nbsp;", n.length); }));
				var newHeight = Math.min(Math.max(div.height(), minHeight), options.maxHeight);
				$textarea.css({ overflow: (newHeight == options.maxHeight) ? "auto" : "hidden" });
				$textarea.css({ height: newHeight + "px" });
			};
			checkSize();
		});
	};
})(jQuery);

(function($) {
	$.fn.forceMaxlength = function(o) {
		var defaults = {
		};
		var options = $.extend(defaults, o);

		return this.each(function() {
			try{if(this.forcingMaxlength) return;}catch(Ex){}
			this.forcingmaxlength = true;
			var $textarea   = $(this),
				maxLength  = parseInt($textarea.attr("maxlength"), 10);

			if(maxLength <= 0) return;

			var checkLen = function() {
				var str = $textarea.val();
				if(str.length > maxLength) {
					str = str.substring(0, maxLength);
					$textarea.val(str);
				}
			};
			checkLen();
			$textarea.keydown(checkLen);
			$textarea.keyup(checkLen);
			$textarea.keypress(checkLen);
		});
	};
})(jQuery);
function saveChangedForms() {
	$('form.notifychanges').each(function() {
		var $this = this;
		if($this.hasChanged) {
			var firstsubm = false;
			var imgsubm = false;
			$('input', $this).each(function(){
				if(this.type.toUpperCase() == 'IMAGE') {
					firstsubm = this;
					imgsubm = true;
					return false;
				}
				if(this.type.toUpperCase() == 'SUBMIT') {
					firstsubm = this;
					return false;
				}
			});
			if(firstsubm) {
				var data = {};
				if(!imgsubm)
					data[firstsubm.name] = firstsubm.value;
				else {
					data[firstsubm.name+'.x'] = 0;
					data[firstsubm.name+'.y'] = 0;
				}
				simpleAjaxSubmit($this, data);
			}
		}
	});
	return false;
}

