/*
*
* Copyright (c) 2006-2008 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.3
* 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) {
        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;
		    }
		}
	);
        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 value  Which options should be selected
    * can be a string or regular expression
    * @param    Boolean clear  Clear existing selected options, default false
    * @example  $("#myselect").selectOptions("val1"); // with the value 'val1'
    * @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);
        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.find("option:selected").each(
		function() {
		    v[v.length] = this.value;
		}
	);
        return v;
    };

    /**
    * 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);;/*
 * jQuery validation plug-in 1.5.2
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
 * http://docs.jquery.com/Plugins/Validation
 *
 * Copyright (c) 2006 - 2008 Jörn Zaefferer
 *
 * $Id: jquery.validate.js 6243 2009-02-19 11:40:49Z joern.zaefferer $
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function($) {

$.extend($.fn, {
	// http://docs.jquery.com/Plugins/Validation/validate
	validate: function( options ) {
		
		// if nothing is selected, return nothing; can't chain anyway
		if (!this.length) {
			options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
			return;
		}
		
		// check if a validator for this form was already created
		var validator = $.data(this[0], 'validator');
		if ( validator ) {
			return validator;
		}
		
		validator = new $.validator( options, this[0] );
		$.data(this[0], 'validator', validator); 
		
		if ( validator.settings.onsubmit ) {
		
			// allow suppresing validation by adding a cancel class to the submit button
			this.find("input, button").filter(".cancel").click(function() {
				validator.cancelSubmit = true;
			});
		
			// validate the form on submit
			this.submit( function( event ) {
				if ( validator.settings.debug )
					// prevent form submit to be able to see console output
					event.preventDefault();
					
				function handle() {
					if ( validator.settings.submitHandler ) {
						validator.settings.submitHandler.call( validator, validator.currentForm );
						return false;
					}
					return true;
				}
					
				// prevent submit for invalid forms or custom submit handlers
				if ( validator.cancelSubmit ) {
					validator.cancelSubmit = false;
					return handle();
				}
				if ( validator.form() ) {
					if ( validator.pendingRequest ) {
						validator.formSubmitted = true;
						return false;
					}
					return handle();
				} else {
					validator.focusInvalid();
					return false;
				}
			});
		}
		
		return validator;
	},
	// http://docs.jquery.com/Plugins/Validation/valid
	valid: function() {
        if ( $(this[0]).is('form')) {
            return this.validate().form();
        } else {
            var valid = false;
            var validator = $(this[0].form).validate();
            this.each(function() {
				valid |= validator.element(this);
            });
            return valid;
        }
    },
	// attributes: space seperated list of attributes to retrieve and remove
	removeAttrs: function(attributes) {
		var result = {},
			$element = this;
		$.each(attributes.split(/\s/), function(index, value) {
			result[value] = $element.attr(value);
			$element.removeAttr(value);
		});
		return result;
	},
	// http://docs.jquery.com/Plugins/Validation/rules
	rules: function(command, argument) {
		var element = this[0];
		
		if (command) {
			var settings = $.data(element.form, 'validator').settings;
			var staticRules = settings.rules;
			var existingRules = $.validator.staticRules(element);
			switch(command) {
			case "add":
				$.extend(existingRules, $.validator.normalizeRule(argument));
				staticRules[element.name] = existingRules;
				if (argument.messages)
					settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
				break;
			case "remove":
				if (!argument) {
					delete staticRules[element.name];
					return existingRules;
				}
				var filtered = {};
				$.each(argument.split(/\s/), function(index, method) {
					filtered[method] = existingRules[method];
					delete existingRules[method];
				});
				return filtered;
			}
		}
		
		var data = $.validator.normalizeRules(
		$.extend(
			{},
			$.validator.metadataRules(element),
			$.validator.classRules(element),
			$.validator.attributeRules(element),
			$.validator.staticRules(element)
		), element);
		
		// make sure required is at front
		if (data.required) {
			var param = data.required;
			delete data.required;
			data = $.extend({required: param}, data);
		}
		
		return data;
	}
});

// Custom selectors
$.extend($.expr[":"], {
	// http://docs.jquery.com/Plugins/Validation/blank
	blank: function(a) {return !$.trim(a.value);},
	// http://docs.jquery.com/Plugins/Validation/filled
	filled: function(a) {return !!$.trim(a.value);},
	// http://docs.jquery.com/Plugins/Validation/unchecked
	unchecked: function(a) {return !a.checked;}
});


$.format = function(source, params) {
	if ( arguments.length == 1 ) 
		return function() {
			var args = $.makeArray(arguments);
			args.unshift(source);
			return $.format.apply( this, args );
		};
	if ( arguments.length > 2 && params.constructor != Array  ) {
		params = $.makeArray(arguments).slice(1);
	}
	if ( params.constructor != Array ) {
		params = [ params ];
	}
	$.each(params, function(i, n) {
		source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
	});
	return source;
};

// constructor for validator
$.validator = function( options, form ) {
	this.settings = $.extend( {}, $.validator.defaults, options );
	this.currentForm = form;
	this.init();
};

$.extend($.validator, {

	defaults: {
		messages: {},
		groups: {},
		rules: {},
		errorClass: "error",
		errorElement: "label",
		focusInvalid: true,
		errorContainer: $( [] ),
		errorLabelContainer: $( [] ),
		onsubmit: true,
		ignore: [],
		ignoreTitle: false,
		onfocusin: function(element) {
			this.lastActive = element;
				
			// hide error label and remove error class on focus if enabled
			if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
				this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass );
				this.errorsFor(element).hide();
			}
		},
		onfocusout: function(element) {
			if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
				this.element(element);
			}
		},
		onkeyup: function(element) {
			if ( element.name in this.submitted || element == this.lastElement ) {
				this.element(element);
			}
		},
		onclick: function(element) {
			if ( element.name in this.submitted )
				this.element(element);
		},
		highlight: function( element, errorClass ) {
			$( element ).addClass( errorClass );
		},
		unhighlight: function( element, errorClass ) {
			$( element ).removeClass( errorClass );
		}
	},

	// http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
	setDefaults: function(settings) {
		$.extend( $.validator.defaults, settings );
	},

	messages: {
		required: "This field is required.",
		remote: "Please fix this field.",
		email: "Please enter a valid email address.",
		url: "Please enter a valid URL.",
		date: "Please enter a valid date.",
		dateISO: "Please enter a valid date (ISO).",
		dateDE: "Bitte geben Sie ein gültiges Datum ein.",
		number: "Please enter a valid number.",
		numberDE: "Bitte geben Sie eine Nummer ein.",
		digits: "Please enter only digits",
		creditcard: "Please enter a valid credit card number.",
		equalTo: "Please enter the same value again.",
		accept: "Please enter a value with a valid extension.",
		maxlength: $.format("Please enter no more than {0} characters."),
		minlength: $.format("Please enter at least {0} characters."),
		rangelength: $.format("Please enter a value between {0} and {1} characters long."),
		range: $.format("Please enter a value between {0} and {1}."),
		max: $.format("Please enter a value less than or equal to {0}."),
		min: $.format("Please enter a value greater than or equal to {0}.")
	},
	
	autoCreateRanges: false,
	
	prototype: {
		
		init: function() {
			this.labelContainer = $(this.settings.errorLabelContainer);
			this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
			this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
			this.submitted = {};
			this.valueCache = {};
			this.pendingRequest = 0;
			this.pending = {};
			this.invalid = {};
			this.reset();
			
			var groups = (this.groups = {});
			$.each(this.settings.groups, function(key, value) {
				$.each(value.split(/\s/), function(index, name) {
					groups[name] = key;
				});
			});
			var rules = this.settings.rules;
			$.each(rules, function(key, value) {
				rules[key] = $.validator.normalizeRule(value);
			});
			
			function delegate(event) {
				var validator = $.data(this[0].form, "validator");
				validator.settings["on" + event.type] && validator.settings["on" + event.type].call(validator, this[0] );
			}
			$(this.currentForm)
				.delegate("focusin focusout keyup", ":text, :password, :file, select, textarea", delegate)
				.delegate("click", ":radio, :checkbox", delegate);

			if (this.settings.invalidHandler)
				$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/form
		form: function() {
			this.checkForm();
			$.extend(this.submitted, this.errorMap);
			this.invalid = $.extend({}, this.errorMap);
			if (!this.valid())
				$(this.currentForm).triggerHandler("invalid-form", [this]);
			this.showErrors();
			return this.valid();
		},
		
		checkForm: function() {
			this.prepareForm();
			for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
				this.check( elements[i] );
			}
			return this.valid(); 
		},
		
		// http://docs.jquery.com/Plugins/Validation/Validator/element
		element: function( element ) {
			element = this.clean( element );
			this.lastElement = element;
			this.prepareElement( element );
			this.currentElements = $(element);
			var result = this.check( element );
			if ( result ) {
				delete this.invalid[element.name];
			} else {
				this.invalid[element.name] = true;
			}
			if ( !this.numberOfInvalids() ) {
				// Hide error containers on last error
				this.toHide = this.toHide.add( this.containers );
			}
			this.showErrors();
			return result;
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/showErrors
		showErrors: function(errors) {
			if(errors) {
				// add items to error list and map
				$.extend( this.errorMap, errors );
				this.errorList = [];
				for ( var name in errors ) {
					this.errorList.push({
						message: errors[name],
						element: this.findByName(name)[0]
					});
				}
				// remove items from success list
				this.successList = $.grep( this.successList, function(element) {
					return !(element.name in errors);
				});
			}
			this.settings.showErrors
				? this.settings.showErrors.call( this, this.errorMap, this.errorList )
				: this.defaultShowErrors();
		},
		
		// http://docs.jquery.com/Plugins/Validation/Validator/resetForm
		resetForm: function() {
			if ( $.fn.resetForm )
				$( this.currentForm ).resetForm();
			this.submitted = {};
			this.prepareForm();
			this.hideErrors();
			this.elements().removeClass( this.settings.errorClass );
		},
		
		numberOfInvalids: function() {
			return this.objectLength(this.invalid);
		},
		
		objectLength: function( obj ) {
			var count = 0;
			for ( var i in obj )
				count++;
			return count;
		},
		
		hideErrors: function() {
			this.addWrapper( this.toHide ).hide();
		},
		
		valid: function() {
			return this.size() == 0;
		},
		
		size: function() {
			return this.errorList.length;
		},
		
		focusInvalid: function() {
			if( this.settings.focusInvalid ) {
				try {
					$(this.findLastActive() || this.errorList.length && this.errorList[0].element || []).filter(":visible").focus();
				} catch(e) {
					// ignore IE throwing errors when focusing hidden elements
				}
			}
		},
		
		findLastActive: function() {
			var lastActive = this.lastActive;
			return lastActive && $.grep(this.errorList, function(n) {
				return n.element.name == lastActive.name;
			}).length == 1 && lastActive;
		},
		
		elements: function() {
			var validator = this,
				rulesCache = {};
			
			// select all valid inputs inside the form (no submit or reset buttons)
			// workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved
			return $([]).add(this.currentForm.elements)
			.filter(":input")
			.not(":submit, :reset, :image, [disabled]")
			.not( this.settings.ignore )
			.filter(function() {
				!this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
			
				// select only the first element for each name, and only those with rules specified
				if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
					return false;
				
				rulesCache[this.name] = true;
				return true;
			});
		},
		
		clean: function( selector ) {
			return $( selector )[0];
		},
		
		errors: function() {
			return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
		},
		
		reset: function() {
			this.successList = [];
			this.errorList = [];
			this.errorMap = {};
			this.toShow = $([]);
			this.toHide = $([]);
			this.formSubmitted = false;
			this.currentElements = $([]);
		},
		
		prepareForm: function() {
			this.reset();
			this.toHide = this.errors().add( this.containers );
		},
		
		prepareElement: function( element ) {
			this.reset();
			this.toHide = this.errorsFor(element);
		},
	
		check: function( element ) {
			element = this.clean( element );
			
			// if radio/checkbox, validate first element in group instead
			if (this.checkable(element)) {
				element = this.findByName( element.name )[0];
			}
			
			var rules = $(element).rules();
			var dependencyMismatch = false;
			for( method in rules ) {
				var rule = { method: method, parameters: rules[method] };
				try {
					var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
					
					// if a method indicates that the field is optional and therefore valid,
					// don't mark it as valid when there are no other rules
					if ( result == "dependency-mismatch" ) {
						dependencyMismatch = true;
						continue;
					}
					dependencyMismatch = false;
					
					if ( result == "pending" ) {
						this.toHide = this.toHide.not( this.errorsFor(element) );
						return;
					}
					
					if( !result ) {
						this.formatAndAdd( element, rule );
						return false;
					}
				} catch(e) {
					this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
						 + ", check the '" + rule.method + "' method");
					throw e;
				}
			}
			if (dependencyMismatch)
				return;
			if ( this.objectLength(rules) )
				this.successList.push(element);
			return true;
		},
		
		// return the custom message for the given element and validation method
		// specified in the element's "messages" metadata
		customMetaMessage: function(element, method) {
			if (!$.metadata)
				return;
			
			var meta = this.settings.meta
				? $(element).metadata()[this.settings.meta]
				: $(element).metadata();
			
			return meta && meta.messages && meta.messages[method];
		},
		
		// return the custom message for the given element name and validation method
		customMessage: function( name, method ) {
			var m = this.settings.messages[name];
			return m && (m.constructor == String
				? m
				: m[method]);
		},
		
		// return the first defined argument, allowing empty strings
		findDefined: function() {
			for(var i = 0; i < arguments.length; i++) {
				if (arguments[i] !== undefined)
					return arguments[i];
			}
			return undefined;
		},
		
		defaultMessage: function( element, method) {
			return this.findDefined(
				this.customMessage( element.name, method ),
				this.customMetaMessage( element, method ),
				// title is never undefined, so handle empty string as undefined
				!this.settings.ignoreTitle && element.title || undefined,
				$.validator.messages[method],
				"<strong>Warning: No message defined for " + element.name + "</strong>"
			);
		},
		
		formatAndAdd: function( element, rule ) {
			var message = this.defaultMessage( element, rule.method );
			if ( typeof message == "function" ) 
				message = message.call(this, rule.parameters, element);
			this.errorList.push({
				message: message,
				element: element
			});
			this.errorMap[element.name] = message;
			this.submitted[element.name] = message;
		},
		
		addWrapper: function(toToggle) {
			if ( this.settings.wrapper )
				toToggle = toToggle.add( toToggle.parents( this.settings.wrapper ) );
			return toToggle;
		},
		
		defaultShowErrors: function() {
			for ( var i = 0; this.errorList[i]; i++ ) {
				var error = this.errorList[i];
				this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass );
				this.showLabel( error.element, error.message );
			}
			if( this.errorList.length ) {
				this.toShow = this.toShow.add( this.containers );
			}
			if (this.settings.success) {
				for ( var i = 0; this.successList[i]; i++ ) {
					this.showLabel( this.successList[i] );
				}
			}
			if (this.settings.unhighlight) {
				for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
					this.settings.unhighlight.call( this, elements[i], this.settings.errorClass );
				}
			}
			this.toHide = this.toHide.not( this.toShow );
			this.hideErrors();
			this.addWrapper( this.toShow ).show();
		},
		
		validElements: function() {
			return this.currentElements.not(this.invalidElements());
		},
		
		invalidElements: function() {
			return $(this.errorList).map(function() {
				return this.element;
			});
		},
		
		showLabel: function(element, message) {
			var label = this.errorsFor( element );
			if ( label.length ) {
				// refresh error/success class
				label.removeClass().addClass( this.settings.errorClass );
			
				// check if we have a generated label, replace the message then
				label.attr("generated") && label.html(message);
			} else {
				// create label
				label = $("<" + this.settings.errorElement + "/>")
					.attr({"for":  this.idOrName(element), generated: true})
					.addClass(this.settings.errorClass)
					.html(message || "");
				if ( this.settings.wrapper ) {
					// make sure the element is visible, even in IE
					// actually showing the wrapped element is handled elsewhere
					label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
				}
				if ( !this.labelContainer.append(label).length )
					this.settings.errorPlacement
						? this.settings.errorPlacement(label, $(element) )
						: label.insertAfter(element);
			}
			if ( !message && this.settings.success ) {
				label.text("");
				typeof this.settings.success == "string"
					? label.addClass( this.settings.success )
					: this.settings.success( label );
			}
			this.toShow = this.toShow.add(label);
		},
		
		errorsFor: function(element) {
			return this.errors().filter("[for='" + this.idOrName(element) + "']");
		},
		
		idOrName: function(element) {
			return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
		},

		checkable: function( element ) {
			return /radio|checkbox/i.test(element.type);
		},
		
		findByName: function( name ) {
			// select by name and filter by form for performance over form.find("[name=...]")
			var form = this.currentForm;
			return $(document.getElementsByName(name)).map(function(index, element) {
				return element.form == form && element.name == name && element  || null;
			});
		},
		
		getLength: function(value, element) {
			switch( element.nodeName.toLowerCase() ) {
			case 'select':
				return $("option:selected", element).length;
			case 'input':
				if( this.checkable( element) )
					return this.findByName(element.name).filter(':checked').length;
			}
			return value.length;
		},
	
		depend: function(param, element) {
			return this.dependTypes[typeof param]
				? this.dependTypes[typeof param](param, element)
				: true;
		},
	
		dependTypes: {
			"boolean": function(param, element) {
				return param;
			},
			"string": function(param, element) {
				return !!$(param, element.form).length;
			},
			"function": function(param, element) {
				return param(element);
			}
		},
		
		optional: function(element) {
			return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
		},
		
		startRequest: function(element) {
			if (!this.pending[element.name]) {
				this.pendingRequest++;
				this.pending[element.name] = true;
			}
		},
		
		stopRequest: function(element, valid) {
			this.pendingRequest--;
			// sometimes synchronization fails, make sure pendingRequest is never < 0
			if (this.pendingRequest < 0)
				this.pendingRequest = 0;
			delete this.pending[element.name];
			if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
				$(this.currentForm).submit();
			} else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
				$(this.currentForm).triggerHandler("invalid-form", [this]);
			}
		},
		
		previousValue: function(element) {
			return $.data(element, "previousValue") || $.data(element, "previousValue", previous = {
				old: null,
				valid: true,
				message: this.defaultMessage( element, "remote" )
			});
		}
		
	},
	
	classRuleSettings: {
		required: {required: true},
		email: {email: true},
		url: {url: true},
		date: {date: true},
		dateISO: {dateISO: true},
		dateDE: {dateDE: true},
		number: {number: true},
		numberDE: {numberDE: true},
		digits: {digits: true},
		creditcard: {creditcard: true}
	},
	
	addClassRules: function(className, rules) {
		className.constructor == String ?
			this.classRuleSettings[className] = rules :
			$.extend(this.classRuleSettings, className);
	},
	
	classRules: function(element) {
		var rules = {};
		var classes = $(element).attr('class');
		classes && $.each(classes.split(' '), function() {
			if (this in $.validator.classRuleSettings) {
				$.extend(rules, $.validator.classRuleSettings[this]);
			}
		});
		return rules;
	},
	
	attributeRules: function(element) {
		var rules = {};
		var $element = $(element);
		
		for (method in $.validator.methods) {
			var value = $element.attr(method);
			if (value) {
				rules[method] = value;
			}
		}
		
		// maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
		if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
			delete rules.maxlength;
		}
		
		return rules;
	},
	
	metadataRules: function(element) {
		if (!$.metadata) return {};
		
		var meta = $.data(element.form, 'validator').settings.meta;
		return meta ?
			$(element).metadata()[meta] :
			$(element).metadata();
	},
	
	staticRules: function(element) {
		var rules = {};
		var validator = $.data(element.form, 'validator');
		if (validator.settings.rules) {
			rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
		}
		return rules;
	},
	
	normalizeRules: function(rules, element) {
		// handle dependency check
		$.each(rules, function(prop, val) {
			// ignore rule when param is explicitly false, eg. required:false
			if (val === false) {
				delete rules[prop];
				return;
			}
			if (val.param || val.depends) {
				var keepRule = true;
				switch (typeof val.depends) {
					case "string":
						keepRule = !!$(val.depends, element.form).length;
						break;
					case "function":
						keepRule = val.depends.call(element, element);
						break;
				}
				if (keepRule) {
					rules[prop] = val.param !== undefined ? val.param : true;
				} else {
					delete rules[prop];
				}
			}
		});
		
		// evaluate parameters
		$.each(rules, function(rule, parameter) {
			rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
		});
		
		// clean number parameters
		$.each(['minlength', 'maxlength', 'min', 'max'], function() {
			if (rules[this]) {
				rules[this] = Number(rules[this]);
			}
		});
		$.each(['rangelength', 'range'], function() {
			if (rules[this]) {
				rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
			}
		});
		
		if ($.validator.autoCreateRanges) {
			// auto-create ranges
			if (rules.min && rules.max) {
				rules.range = [rules.min, rules.max];
				delete rules.min;
				delete rules.max;
			}
			if (rules.minlength && rules.maxlength) {
				rules.rangelength = [rules.minlength, rules.maxlength];
				delete rules.minlength;
				delete rules.maxlength;
			}
		}
		
		// To support custom messages in metadata ignore rule methods titled "messages"
		if (rules.messages) {
			delete rules.messages
		}
		
		return rules;
	},
	
	// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
	normalizeRule: function(data) {
		if( typeof data == "string" ) {
			var transformed = {};
			$.each(data.split(/\s/), function() {
				transformed[this] = true;
			});
			data = transformed;
		}
		return data;
	},
	
	// http://docs.jquery.com/Plugins/Validation/Validator/addMethod
	addMethod: function(name, method, message) {
		$.validator.methods[name] = method;
		$.validator.messages[name] = message;
		if (method.length < 3) {
			$.validator.addClassRules(name, $.validator.normalizeRule(name));
		}
	},

	methods: {

		// http://docs.jquery.com/Plugins/Validation/Methods/required
		required: function(value, element, param) {
			// check if dependency is met
			if ( !this.depend(param, element) )
				return "dependency-mismatch";
			switch( element.nodeName.toLowerCase() ) {
			case 'select':
				var options = $("option:selected", element);
				return options.length > 0 && ( element.type == "select-multiple" || ($.browser.msie && !(options[0].attributes['value'].specified) ? options[0].text : options[0].value).length > 0);
			case 'input':
				if ( this.checkable(element) )
					return this.getLength(value, element) > 0;
			default:
				return $.trim(value).length > 0;
			}
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/remote
		remote: function(value, element, param) {
			if ( this.optional(element) )
				return "dependency-mismatch";
			
			var previous = this.previousValue(element);
			
			if (!this.settings.messages[element.name] )
				this.settings.messages[element.name] = {};
			this.settings.messages[element.name].remote = typeof previous.message == "function" ? previous.message(value) : previous.message;
			
			param = typeof param == "string" && {url:param} || param; 
			
			if ( previous.old !== value ) {
				previous.old = value;
				var validator = this;
				this.startRequest(element);
				var data = {};
				data[element.name] = value;
				$.ajax($.extend(true, {
					url: param,
					mode: "abort",
					port: "validate" + element.name,
					dataType: "json",
					data: data,
					success: function(response) {
						if ( response ) {
							var submitted = validator.formSubmitted;
							validator.prepareElement(element);
							validator.formSubmitted = submitted;
							validator.successList.push(element);
							validator.showErrors();
						} else {
							var errors = {};
							errors[element.name] =  response || validator.defaultMessage( element, "remote" );
							validator.showErrors(errors);
						}
						previous.valid = response;
						validator.stopRequest(element, response);
					}
				}, param));
				return "pending";
			} else if( this.pending[element.name] ) {
				return "pending";
			}
			return previous.valid;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/minlength
		minlength: function(value, element, param) {
			return this.optional(element) || this.getLength($.trim(value), element) >= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/maxlength
		maxlength: function(value, element, param) {
			return this.optional(element) || this.getLength($.trim(value), element) <= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/rangelength
		rangelength: function(value, element, param) {
			var length = this.getLength($.trim(value), element);
			return this.optional(element) || ( length >= param[0] && length <= param[1] );
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/min
		min: function( value, element, param ) {
			return this.optional(element) || value >= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/max
		max: function( value, element, param ) {
			return this.optional(element) || value <= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/range
		range: function( value, element, param ) {
			return this.optional(element) || ( value >= param[0] && value <= param[1] );
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/email
		email: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
			return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/url
		url: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
			return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
		},
        
		// http://docs.jquery.com/Plugins/Validation/Methods/date
		date: function(value, element) {
			return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/dateISO
		dateISO: function(value, element) {
			return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/dateDE
		dateDE: function(value, element) {
			return this.optional(element) || /^\d\d?\.\d\d?\.\d\d\d?\d?$/.test(value);
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/number
		number: function(value, element) {
			return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/numberDE
		numberDE: function(value, element) {
			return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(value);
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/digits
		digits: function(value, element) {
			return this.optional(element) || /^\d+$/.test(value);
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/creditcard
		// based on http://en.wikipedia.org/wiki/Luhn
		creditcard: function(value, element) {
			if ( this.optional(element) )
				return "dependency-mismatch";
			// accept only digits and dashes
			if (/[^0-9-]+/.test(value))
				return false;
			var nCheck = 0,
				nDigit = 0,
				bEven = false;

			value = value.replace(/\D/g, "");

			for (n = value.length - 1; n >= 0; n--) {
				var cDigit = value.charAt(n);
				var nDigit = parseInt(cDigit, 10);
				if (bEven) {
					if ((nDigit *= 2) > 9)
						nDigit -= 9;
				}
				nCheck += nDigit;
				bEven = !bEven;
			}

			return (nCheck % 10) == 0;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/accept
		accept: function(value, element, param) {
			param = typeof param == "string" ? param : "png|jpe?g|gif";
			return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i")); 
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
		equalTo: function(value, element, param) {
			return value == $(param).val();
		}
		
	}
	
});

})(jQuery);

// ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() 
;(function($) {
	var ajax = $.ajax;
	var pendingRequests = {};
	$.ajax = function(settings) {
		// create settings for compatibility with ajaxSetup
		settings = $.extend(settings, $.extend({}, $.ajaxSettings, settings));
		var port = settings.port;
		if (settings.mode == "abort") {
			if ( pendingRequests[port] ) {
				pendingRequests[port].abort();
			}
			return (pendingRequests[port] = ajax.apply(this, arguments));
		}
		return ajax.apply(this, arguments);
	};
})(jQuery);

// provides cross-browser focusin and focusout events
// IE has native support, in other browsers, use event caputuring (neither bubbles)

// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target 

// provides triggerEvent(type: String, target: Element) to trigger delegated events
;(function($) {
	$.each({
		focus: 'focusin',
		blur: 'focusout'	
	}, function( original, fix ){
		$.event.special[fix] = {
			setup:function() {
				if ( $.browser.msie ) return false;
				this.addEventListener( original, $.event.special[fix].handler, true );
			},
			teardown:function() {
				if ( $.browser.msie ) return false;
				this.removeEventListener( original,
				$.event.special[fix].handler, true );
			},
			handler: function(e) {
				arguments[0] = $.event.fix(e);
				arguments[0].type = fix;
				return $.event.handle.apply(this, arguments);
			}
		};
	});
	$.extend($.fn, {
		delegate: function(type, delegate, handler) {
			return this.bind(type, function(event) {
				var target = $(event.target);
				if (target.is(delegate)) {
					return handler.apply(target, arguments);
				}
			});
		},
		triggerEvent: function(type, target) {
			return this.triggerHandler(type, [$.event.fix({ type: type, target: target })]);
		}
	})
})(jQuery);
;/*
* jQuery Media Plugin for converting elements into rich media content.
*
* Examples and documentation at: http://malsup.com/jquery/media/
* Copyright (c) 2007-2008 M. Alsup
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* @author: M. Alsup
* @version: 0.90 (10-MAY-2009)
* @requires jQuery v1.1.2 or later
* $Id: jquery.media.js 2460 2007-07-23 02:53:15Z malsup $
*
* Supported Media Players:
*    - Flash
*    - Quicktime
*    - Real Player
*    - Silverlight
*    - Windows Media Player
*    - iframe
*
* Supported Media Formats:
*   Any types supported by the above players, such as:
*     Video: asf, avi, flv, mov, mpg, mpeg, mp4, qt, smil, swf, wmv, 3g2, 3gp
*     Audio: aif, aac, au, gsm, mid, midi, mov, mp3, m4a, snd, rm, wav, wma
*     Other: bmp, html, pdf, psd, qif, qtif, qti, tif, tiff, xaml
*
* Thanks to Mark Hicken and Brent Pedersen for helping me debug this on the Mac!
* Thanks to Dan Rossi for numerous bug reports and code bits!
* Thanks to Skye Giordano for several great suggestions!
*/
; (function($) {

	/**
	* Chainable method for converting elements into rich media.
	*
	* @param options
	* @param callback fn invoked for each matched element before conversion
	* @param callback fn invoked for each matched element after conversion
	*/
	$.fn.media = function(options, f1, f2) {
		return this.each(function() {
			if (typeof options == 'function') {
				f2 = f1;
				f1 = options;
				options = {};
			}
			var o = getSettings(this, options);
			// pre-conversion callback, passes original element and fully populated options
			if (typeof f1 == 'function') f1(this, o);

			var r = getTypesRegExp();
			var m = r.exec(o.src.toLowerCase()) || [''];

			o.type ? m[0] = o.type : m.shift();
			for (var i = 0; i < m.length; i++) {
				fn = m[i].toLowerCase();
				if (isDigit(fn[0])) fn = 'fn' + fn; // fns can't begin with numbers
				if (!$.fn.media[fn])
					continue;  // unrecognized media type
				// normalize autoplay settings
				var player = $.fn.media[fn + '_player'];
				if (!o.params) o.params = {};
				if (player) {
					var num = player.autoplayAttr == 'autostart';
					o.params[player.autoplayAttr || 'autoplay'] = num ? (o.autoplay ? 1 : 0) : o.autoplay ? true : false;
				}
				var $div = $.fn.media[fn](this, o);

				$div.css('backgroundColor', o.bgColor).width(o.width);
				// post-conversion callback, passes original element, new div element and fully populated options
				if (typeof f2 == 'function') f2(this, $div[0], o, player.name);
				break;
			}
		});
	};

	/**
	* Non-chainable method for adding or changing file format / player mapping
	* @name mapFormat
	* @param String format File format extension (ie: mov, wav, mp3)
	* @param String player Player name to use for the format (one of: flash, quicktime, realplayer, winmedia, silverlight or iframe
	*/
	$.fn.media.mapFormat = function(format, player) {
		if (!format || !player || !$.fn.media.defaults.players[player]) return; // invalid
		format = format.toLowerCase();
		if (isDigit(format[0])) format = 'fn' + format;
		$.fn.media[format] = $.fn.media[player];
		$.fn.media[format + '_player'] = $.fn.media.defaults.players[player];
	};

	// global defautls; override as needed
	$.fn.media.defaults = {
		width: 400,
		height: 400,
		autoplay: 0,         // normalized cross-player setting
		bgColor: '#ffffff', // background color
		params: { wmode: 'transparent' },  // added to object element as param elements; added to embed element as attrs
		attrs: {},        // added to object and embed elements as attrs
		flvKeyName: 'file',    // key used for object src param (thanks to Andrea Ercolino)
		flashvars: {},        // added to flash content as flashvars param/attr
		flashVersion: '7',       // required flash version
		expressInstaller: null,   // src for express installer

		// default flash video and mp3 player (@see: http://jeroenwijering.com/?item=Flash_Media_Player)
		flvPlayer: 'mediaplayer.swf',
		mp3Player: 'mediaplayer.swf',

		// @see http://msdn2.microsoft.com/en-us/library/bb412401.aspx
		silverlight: {
			inplaceInstallPrompt: 'true', // display in-place install prompt?
			isWindowless: 'true', // windowless mode (false for wrapping markup)
			framerate: '24',   // maximum framerate
			version: '0.9',  // Silverlight version
			onError: null,   // onError callback
			onLoad: null,   // onLoad callback
			initParams: null,   // object init params
			userContext: null    // callback arg passed to the load callback
		}
	};

	// Media Players; think twice before overriding
	$.fn.media.defaults.players = {
		flash: {
			name: 'flash',
			types: 'flv,mp3,swf',
			oAttrs: {
				classid: 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
				type: 'application/x-oleobject',
				codebase: 'http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=' + $.fn.media.defaults.flashVersion
			},
			eAttrs: {
				type: 'application/x-shockwave-flash',
				pluginspage: 'http://www.adobe.com/go/getflashplayer'
			}
		},
		quicktime: {
			name: 'quicktime',
			types: 'aif,aiff,aac,au,bmp,gsm,mov,mid,midi,mpg,mpeg,mp4,m4a,psd,qt,qtif,qif,qti,snd,tif,tiff,wav,3g2,3gp',
			oAttrs: {
				classid: 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B',
				codebase: 'http://www.apple.com/qtactivex/qtplugin.cab'
			},
			eAttrs: {
				pluginspage: 'http://www.apple.com/quicktime/download/'
			}
		},
		realplayer: {
			name: 'real',
			types: 'ra,ram,rm,rpm,rv,smi,smil',
			autoplayAttr: 'autostart',
			oAttrs: {
				classid: 'clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'
			},
			eAttrs: {
				type: 'audio/x-pn-realaudio-plugin',
				pluginspage: 'http://www.real.com/player/'
			}
		},
		winmedia: {
			name: 'winmedia',
			types: 'asx,asf,avi,wma,wmv',
			autoplayAttr: 'autostart',
			oUrl: 'url',
			oAttrs: {
				classid: 'clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6',
				type: 'application/x-oleobject'
			},
			eAttrs: {
				type: $.browser.mozilla && isFirefoxWMPPluginInstalled() ? 'application/x-ms-wmp' : 'application/x-mplayer2',
				pluginspage: 'http://www.microsoft.com/Windows/MediaPlayer/'
			}
		},
		// special cases
		iframe: {
			name: 'iframe',
			types: 'html,pdf'
		},
		silverlight: {
			name: 'silverlight',
			types: 'xaml'
		}
	};

	//
	//  everything below here is private
	//


	// detection script for FF WMP plugin (http://www.therossman.org/experiments/wmp_play.html)
	// (hat tip to Mark Ross for this script)
	function isFirefoxWMPPluginInstalled() {
		var plugs = navigator.plugins;
		for (i = 0; i < plugs.length; i++) {
			var plugin = plugs[i];
			if (plugin['filename'] == 'np-mswmp.dll')
				return true;
		}
		return false;
	}

	var counter = 1;

	for (var player in $.fn.media.defaults.players) {
		var types = $.fn.media.defaults.players[player].types;
		$.each(types.split(','), function(i, o) {
			if (isDigit(o[0])) o = 'fn' + o;
			$.fn.media[o] = $.fn.media[player] = getGenerator(player);
			$.fn.media[o + '_player'] = $.fn.media.defaults.players[player];
		});
	};

	function getTypesRegExp() {
		var types = '';
		for (var player in $.fn.media.defaults.players) {
			if (types.length) types += ',';
			types += $.fn.media.defaults.players[player].types;
		};
		return new RegExp('\\.(' + types.replace(/,/ig, '|') + ')\\b');
	};

	function getGenerator(player) {
		return function(el, options) {
			return generate(el, options, player);
		};
	};

	function isDigit(c) {
		return '0123456789'.indexOf(c) > -1;
	};

	// flatten all possible options: global defaults, meta, option obj
	function getSettings(el, options) {
		options = options || {};
		var $el = $(el);
		var cls = el.className || '';
		// support metadata plugin (v1.0 and v2.0)
		var meta = $.metadata ? $el.metadata() : $.meta ? $el.data() : {};
		meta = meta || {};
		var w = meta.width || parseInt(((cls.match(/w:(\d+)/) || [])[1] || 0));
		var h = meta.height || parseInt(((cls.match(/h:(\d+)/) || [])[1] || 0));

		if (w) meta.width = w;
		if (h) meta.height = h;
		if (cls) meta.cls = cls;

		var a = $.fn.media.defaults;
		var b = options;
		var c = meta;

		var p = { params: { bgColor: options.bgColor || $.fn.media.defaults.bgColor} };
		var opts = $.extend({}, a, b, c);
		$.each(['attrs', 'params', 'flashvars', 'silverlight'], function(i, o) {
			opts[o] = $.extend({}, p[o] || {}, a[o] || {}, b[o] || {}, c[o] || {});
		});

		if (typeof opts.caption == 'undefined') opts.caption = $el.text();

		// make sure we have a source!
		opts.src = opts.src || $el.attr('href') || $el.attr('src') || 'unknown';
		return opts;
	};

	//
	//  Flash Player
	//

	// generate flash using SWFObject library if possible
	$.fn.media.swf = function(el, opts) {
		if (!window.SWFObject && !window.swfobject) {
			// roll our own
			if (opts.flashvars) {
				var a = [];
				for (var f in opts.flashvars)
					a.push(f + '=' + opts.flashvars[f]);
				if (!opts.params) opts.params = {};
				opts.params.flashvars = a.join('&');
			}
			return generate(el, opts, 'flash');
		}

		var id = el.id ? (' id="' + el.id + '"') : '';
		var cls = opts.cls ? (' class="' + opts.cls + '"') : '';
		var $div = $('<div' + id + cls + '>');

		// swfobject v2+
		if (window.swfobject) {
			$(el).after($div).appendTo($div);
			if (!el.id) el.id = 'movie_player_' + counter++;

			// replace el with swfobject content
			swfobject.embedSWF(opts.src, el.id, opts.width, opts.height, opts.flashVersion,
            opts.expressInstaller, opts.flashvars, opts.params, opts.attrs);
		}
		// swfobject < v2
		else {
			$(el).after($div).remove();
			var so = new SWFObject(opts.src, 'movie_player_' + counter++, opts.width, opts.height, opts.flashVersion, opts.bgColor);
			if (opts.expressInstaller) so.useExpressInstall(opts.expressInstaller);

			for (var p in opts.params)
				if (p != 'bgColor') so.addParam(p, opts.params[p]);
			for (var f in opts.flashvars)
				so.addVariable(f, opts.flashvars[f]);
			so.write($div[0]);
		}

		if (opts.caption) $('<div>').appendTo($div).html(opts.caption);
		return $div;
	};

	// map flv and mp3 files to the swf player by default
	$.fn.media.flv = $.fn.media.mp3 = function(el, opts) {
		var src = opts.src;
		var player = /\.mp3\b/i.test(src) ? $.fn.media.defaults.mp3Player : $.fn.media.defaults.flvPlayer;
		var key = opts.flvKeyName;
		src = encodeURIComponent(src);
		opts.src = player;
		opts.src = opts.src + '?' + key + '=' + (src);
		var srcObj = {};
		srcObj[key] = src;
		opts.flashvars = $.extend({}, srcObj, opts.flashvars);
		return $.fn.media.swf(el, opts);
	};

	//
	//  Silverlight
	//
	$.fn.media.xaml = function(el, opts) {
		if (!window.Sys || !window.Sys.Silverlight) {
			if ($.fn.media.xaml.warning) return;
			$.fn.media.xaml.warning = 1;
			alert('You must include the Silverlight.js script.');
			return;
		}

		var props = {
			width: opts.width,
			height: opts.height,
			background: opts.bgColor,
			inplaceInstallPrompt: opts.silverlight.inplaceInstallPrompt,
			isWindowless: opts.silverlight.isWindowless,
			framerate: opts.silverlight.framerate,
			version: opts.silverlight.version
		};
		var events = {
			onError: opts.silverlight.onError,
			onLoad: opts.silverlight.onLoad
		};

		var id1 = el.id ? (' id="' + el.id + '"') : '';
		var id2 = opts.id || 'AG' + counter++;
		// convert element to div
		var cls = opts.cls ? (' class="' + opts.cls + '"') : '';
		var $div = $('<div' + id1 + cls + '>');
		$(el).after($div).remove();

		Sys.Silverlight.createObjectEx({
			source: opts.src,
			initParams: opts.silverlight.initParams,
			userContext: opts.silverlight.userContext,
			id: id2,
			parentElement: $div[0],
			properties: props,
			events: events
		});

		if (opts.caption) $('<div>').appendTo($div).html(opts.caption);
		return $div;
	};

	//
	// generate object/embed markup
	//
	function generate(el, opts, player) {
		var $el = $(el);
		var o = $.fn.media.defaults.players[player];

		if (player == 'iframe') {
			var o = $('<iframe' + ' width="' + opts.width + '" height="' + opts.height + '" >');
			o.attr('src', opts.src);
			o.css('backgroundColor', o.bgColor);
		}
		else if ($.browser.msie) {
			var a = ['<object width="' + opts.width + '" height="' + opts.height + '" '];
			for (var key in opts.attrs)
				a.push(key + '="' + opts.attrs[key] + '" ');
			for (var key in o.oAttrs || {}) {
				var v = o.oAttrs[key];
				if (key == 'codebase' && window.location.protocol == 'https:')
					v = v.replace('http', 'https');
				a.push(key + '="' + v + '" ');
			}
			a.push('></ob' + 'ject' + '>');
			var p = ['<param name="' + (o.oUrl || 'src') + '" value="' + opts.src + '">'];
			for (var key in opts.params)
				p.push('<param name="' + key + '" value="' + opts.params[key] + '">');
			var o = document.createElement(a.join(''));
			for (var i = 0; i < p.length; i++)
				o.appendChild(document.createElement(p[i]));
		}
		else {
			var a = ['<embed width="' + opts.width + '" height="' + opts.height + '" style="display:block"'];
			if (opts.src) a.push(' src="' + opts.src + '" ');
			for (var key in opts.attrs)
				a.push(key + '="' + opts.attrs[key] + '" ');
			for (var key in o.eAttrs || {})
				a.push(key + '="' + o.eAttrs[key] + '" ');
			for (var key in opts.params) {
				if (key == 'wmode' && player != 'flash') // FF3/Quicktime borks on wmode
					continue;
				a.push(key + '="' + opts.params[key] + '" ');
			}
			a.push('></em' + 'bed' + '>');
		}
		// convert element to div
		var id = el.id ? (' id="' + el.id + '"') : '';
		var cls = opts.cls ? (' class="' + opts.cls + '"') : '';
		var $div = $('<div' + id + cls + '>');
		$el.after($div).remove();
		($.browser.msie || player == 'iframe') ? $div.append(o) : $div.html(a.join(''));
		if (opts.caption) $('<div>').appendTo($div).html(opts.caption);
		return $div;
	};
})(jQuery);
;(function() {

// Namespacing
if (!window.mvc) window.mvc = function() { };

// This is called by the code generated script
var xforms = window.mvc.xforms = {
	init: function(form) { initChildCons(form, ''); }
};

/** Classes **/
function controlBase(el, con) { }

function group(el, con) {
	initChildCons(con, el.id);
}

function repeat(el, con) {
	this.index = el.childNodes.length - 1;
	$.each(el.childNodes, function(i) {
		this.xforms = new repeatItem(this, i);
		initChildCons(con, el.id + '-' + i);
	});
}
repeat.prototype.insertItem = insertItem;
repeat.prototype.deleteItem = deleteItem;
repeat.prototype.buildTemplate = buildTemplate;

function repeatItem(el, itemIndex) {
	this.el = el;
	this.itemIndex = itemIndex;
}

/** Common functions **/
function getControl(conID) {
	var jq = $('#' + conID);
	if (jq.length > 0) return jq[0];
	// This is commented out because we may need two or more groups with the
	// same ID (even though this is technically illegal in HTML - is there a
	// better way to identify groups or xforms controls in general?)
	//else if (jq.length > 1) throw('XForms: Multiple elements found for ID = ' + conID); 
	else throw('XForms: Element not found for ID = ' + conID);
}

/** Initialization code **/
function initChildCons(con, prefix) {
	$.each(con.cons, function(i, childCon) {
		initControl(childCon, prefix);
	});
}

function initControl(con, prefix) {
	if (prefix.length > 0 && con.id.length > 0) prefix = prefix + '_';
	var conID = prefix + con.id;
	var el = getControl(conID);
	initDom(el, con);

	// Bind actions
	if (con.acts) {
		$.each(con.acts, function(i, action) {
			if (action.type == 0) bindAction(el, action);
			else if (action.type == 2) bindInsertAction(el, action);
			else if (action.type == 3) bindDeleteAction(el, action);
		});
	}
	
	// We are rebuilding, so trigger the event
	$(el).trigger('xforms-rebuild');
}

// Append the xforms information directly to the DOM
function initDom(el, con) {
	el.xforms = createDomControl(el, con);
	el.xforms.con = con;
	el.xforms.el = el;
}

// Create a script instance of an XForm control
function createDomControl(el, con) {
	if (con.type == 0) return new group(el, con);
	else if (con.type == 3) return new repeat(el, con);
	return new controlBase(el, con);
}

// Binds XForm actions to the DOM (using HTML events via jQuery bind)
function bindAction(el, action) {
	// Condition not currently supported, dont really need
	// it as we are calling a custom script function
	eval('var func = ' + action.func);
	$(el).bind(action.ev, func);
}

// Insert specific to a trigger
function bindInsertAction(el, action) {
	eval('var cond = ' + action.cond);
	var repeat = getTriggerRepeat(el, action.ns);
	$(el).bind(action.ev, function() {
		if (cond != null ? cond() : true) {
			var index = getRepeatIndex(repeat, el, action.at);
			repeat.xforms.insertItem(index, action.pos);
		}
	});
}

// Delete specific to a trigger
function bindDeleteAction(el, action) {
	eval('var cond = ' + action.cond);
	var repeat = getTriggerRepeat(el, action.ns);
	$(el).bind(action.ev, function() {
		if (cond != null ? cond() : true) {
			var index = getRepeatIndex(repeat, el, action.at);
			repeat.xforms.deleteItem(index);
		}
	});
}

// Conditionally execute a function
function actionEvent(ev) {
	if (cond != null ? cond() : true) func(ev);
}

/** Repeat functions **/
function insertItem(index, position) {
	if (index == undefined) index = this.el.childNodes.length - 1;
	if (position == undefined) position = 0;

	var template = this.buildTemplate();
	var jq = $(this.el);
	
	if (this.el.childNodes.length == 0) {
		// If repeat is empty, just add it
		jq.html(template);
	} else if (index > -1) {
		var insertItem = $(this.el.childNodes[index]);
		if (position == 1) insertItem.before(template);
		else insertItem.after(template);
	}
	
	initChildCons(this.con, this.el.id + '-' + template.xforms.itemIndex);
	jq.trigger('xforms-insert', [template, index, position]);
	return template;
};

function deleteItem(index) {
	var jq = $(this.el);
	
	if (index > -1) {
		var deleteItem = jq.children().eq(index);
		deleteItem.remove();
		jq.trigger('xforms-delete', [deleteItem[0], index]);
	}
};

function buildTemplate() {
	this.index++;
	var temJQ = $(this.con.itemTemplate);
	var tem = temJQ[0];
	tem.xforms = new repeatItem(tem, this.index);
	var itemID = this.el.id + '-' + this.index;
	reindexAttr(this, temJQ, 'id', itemID);
	reindexAttr(this, temJQ, 'name', itemID);
	reindexAttr(this, temJQ, 'for', itemID);
	return tem;
}

function reindexAttr(repeat, tem, attr, itemID) {
	$(tem).find('[' + attr + '^=' + repeat.con.templateID + ']').each(function() {
		var jqCon = $(this);
		var newID = jqCon.attr(attr).replace(repeat.con.templateID, itemID);
		jqCon.attr(attr, newID);
	});
}

function getRepeatIndex(repeat, el, at) {
	var itemLength = repeat.childNodes.length;
	if (itemLength == 0 || at == 1)
		return 0;

	if (at == 2 && itemLength > 0) {
		return itemLength - 1;
	} else if (at == 0) {
		var tmpEl = el;
		while (tmpEl.xforms == undefined || tmpEl.xforms.itemIndex == undefined) {
			tmpEl = tmpEl.parentNode;
			if (tmpEl == undefined)
				throw("At = current not valid for insert on trigger " + el.id);
		}
		var item = $(tmpEl);
		var repeatIndex = $(repeat).children().index(item);
		return repeatIndex;
	}
}

function getTriggerRepeat(el, nodeset) {
	var conID = el.xforms.con.id;
	var repeatID = el.id.substr(0, el.id.length - conID.length);
	
	if (nodeset.length > 0) repeatID = repeatID + nodeset;
	else repeatID = repeatID.substr(0, repeatID.lastIndexOf('-'));
	
	var dom = getControl(repeatID);
	return dom;
}

})();;var local_BlockUiElementSelector = "#main";

$(document).ready(function() {
	$("form.xform").submit(function() { engine.WorkingDialog(local_BlockUiElementSelector); });
});

function hotel_ToggleDescription(e, hotelId) {
	if (window.event) e = window.event;
	var srcEl = e.srcelement ? e.srcelement : e.target;
	$("span.rest, span.more", $(srcEl).parent().parent()).toggle();
}
function hotel_ToggleModeProducts(hotelId) {
	if($('#hotel_' + hotelId + ' tr.prod-hidden').css("display") == "none")
		$('#hotel_' + hotelId + ' tr.prod-hidden').show();
	else
		$('#hotel_' + hotelId + ' tr.prod-hidden').hide();
}
var wasCurrencyChanged = false;
function currency_OnChange(o) {
	if (o == null || typeof (arHotelIDs) == "undefined" || arHotelIDs.length < 1)
		return;

	var selectedCurrency = o.value;
	for (i = 0; i < arHotelIDs.length; i++) {
		var hotelData = $("table#hotel_" + arHotelIDs[i]).metadata();

		var curFrom = hotelData.curr, curTo = (selectedCurrency == "" ? hotelData.curr : selectedCurrency);
		var exchRate = getExchangeRate(curFrom, curTo);
		if (exchRate < 0)
			return;
		//prepocteme ceny
		$("#hotel_" + arHotelIDs[i] + " span.rate, .hotel-minies li." + arHotelIDs[i] + " span.rate").each(function(i) {
			var v = $(this).metadata().o * exchRate;
			$(this).text(v.toFixed(2));
		});
		//prehodime currency
		$("#hotel_" + arHotelIDs[i] + " span.curr, .hotel-minies li." + arHotelIDs[i] + " span.curr").text(selectedCurrency == "" ? hotelData.curr : selectedCurrency);
	}
	if (false/*@cc_on || @_jscript_version < 5.7@*/)/*IE6*/ $("div.pager a.active:first").focus(); else $(o).blur();
	wasCurrencyChanged = true;
}
var arExchangeRates = new Array();
function getExchangeRate(fromCode, toCode) {
	if (fromCode == toCode) return 1;
	var d = arExchangeRates[fromCode + "_" + toCode];
	if (d == null) {
		var o = $.ajax({
			async: false,
			type: "GET",
			url: "/en/WebService.mvc/GetExchangeRate?currencyCodeFrom=" + fromCode + "&currencyCodeTo=" + toCode,
			contentType: "application/json; charset=utf-8",
			dataType: "json"
		});
		var e = eval("(" + o.responseText + ")");
		if (e.Message) {
			alert("Ajax Error:\n" + e.Message);
			return -1;
		}
		arExchangeRates[fromCode + "_" + toCode] = e;
	}
	return arExchangeRates[fromCode + "_" + toCode];
}

function pageRequest_OnBegin() {
	engine.WorkingDialog(local_BlockUiElementSelector);
}
function pageRequest_OnSuccess() {
	engine.WorkingDialog(local_BlockUiElementSelector, false);
	if (wasCurrencyChanged)
		currency_OnChange(document.getElementById("Currencies"));
	engine.ToolTipsInit();
}
function pageRequest_Failed(o) {
	$.unblockUI();
	alert("Ajax Error.\nPlease, try later, or contact site admnistrator.");
}


function loadHotelPage(url) {
	$('html, body').animate({ scrollTop: 0 }, 'fast');
	engine.WorkingDialog(local_BlockUiElementSelector);
	$.get(url, function(data) {
		$("#hotels").html(data);
		engine.WorkingDialog(local_BlockUiElementSelector, false);
		if (wasCurrencyChanged) currency_OnChange(document.getElementById("Currencies"));
		engine.ToolTipsInit("#hotels");
		mapHotelList_Destroy();
		if ($("input[id$='_IsHotelsMapVisible']").val().toLowerCase() == "true")
			mapHotelList_Show();
	});
}
;$().ready(function() {
	$("form.xf td.value.date input[type='hidden'], table.info input.date-picker[type='hidden']").datepicker({
		mandatory: true,
		gotoCurrent: true,
		changeMonth: true,
		changeYear: true,
		minDate: '+1d',
		maxDate: '+1y +1m +1d',
		showOn: 'both',
		dateFormat: datePicker_DateFormat,
		buttonImageOnly: true,
		buttonImage: datePicker_ImageButton,
		onSelect: function(dateText) {
			avhForm.SetDate(this.id.split("_")[1], $(this).datepicker('getDate'));
		}
	});
	
	$("#AvhSearchRequest_CurrencyCode").change(function() {
		currency_OnChange(this);
		$("#Currencies").selectOptions(this.value, true);
	});
	
	$("#advanced-search-button a").click(function() {
		$(this).toggleClass("hide");
		$("#hotel-search-form-body tr.advanced").toggleClass("hidden");
	});
});


function goAvh(hotelID, rateTypeID, roomID) {
	$("#HotelID").val(hotelID);
	$("#RateTypeID").val(rateTypeID);
	$("#RoomID").val(roomID);
	$("#TimeStampMs").val(new Date().getTime());
	$("#formAvh").submit();
	engine.Working();
	return false;
}

function AvhSearchForm( idPrefix, minDate, maxDate ) {
	this.IdPrefix = idPrefix;
	this.MinDate = minDate; this.MinDate.setHours(0, 0, 0, 0);
	this.MaxDate = maxDate; this.MaxDate.setHours(0, 0, 0, 0);
	this.MaxOccupancy = 5;

	var guestAgeShowIndex = 3;
	var selectMYDSeparator = " / "; //separator v hodnotach selectu

	this.GetOccupancy = function() {
		return $(getIdJQ("Occupancy")).val();
	}

	this.GetDateFromSelects = function(propName) {
		var monthYear = $(getIdJQ(propName + "_MonthYear") + " :selected").text(), s = monthYear.split(selectMYDSeparator);
		var day = $(getIdJQ(propName + "_Day") + " :selected").text(), month = s[0], year = s[1];
		var date = new Date();
		date.setFullYear(year, month - 1, day);
		date.setHours(0, 0, 0, 0);
		return date;
	}

	this.SetDate = function(propName, date) {
		var o = setDate(propName, date);
		this.Validate(o);
		this.MonthYearChanged(o);
	}

	this.Validate = function(o) {
		var propName = o.name.replace(getId(""), "").split("_")[0], fromDate = this.GetDateFromSelects("FromDate"), toDate = this.GetDateFromSelects("ToDate");
		if (fromDate < avhForm.MinDate) { fromDate = avhForm.MinDate; setDate("FromDate", fromDate); } else if (fromDate >= avhForm.MaxDate) { fromDate.setDate(avhForm.MaxDate.getDate() - 1); setDate("FromDate", fromDate); }
		if (toDate <= avhForm.MinDate) { toDate.setDate(avhForm.MinDate.getDate() + 1); setDate("ToDate", toDate); } else if (toDate > avhForm.MaxDate) { toDate = avhForm.MaxDate; setDate("ToDate", toDate); }
		//mame jistotu ze : FromDate = MinDate ... MaxDate-1, ToDate = MinDate+1 ... MaxDate;
		if (fromDate >= toDate) {
			//[30.3.09][#Com_DateFromMustStay]: DateFrom se nema menit, vzdy se podle nej upravi DateTo
/* [#Com_DateFromMustStay]
			if (propName == "FromDate")//zmenil se From -> To = From + 1
[#Com_DateFromMustStay] */
			{
				var newDate = fromDate.add("d", 1);
				if (!$("#ToDate_Day").containsOption(newDate.getDate().toString())) {
					loadDates("ToDate", newDate.getDate(), newDate.getDate(), newDate.getDate());
				}
				//alert("NewTo = " + $.datepicker.formatDate("dd.mm.yy",newDate));
				setDate("ToDate", newDate);
				this.MonthYearChanged(document.getElementById(getId("ToDate_MonthYear")));
			}
/* [#Com_DateFromMustStay]
			else {//zmenil se To -> From = To - 1
				var newDate = toDate.add("d", -1);
				if (!$("#FromDate_Day").containsOption(newDate.getDate().toString())) {
					loadDates("FromDate", newDate.getDate(), newDate.getDate(), newDate.getDate());
				}
				setDate("FromDate", newDate);
				this.MonthYearChanged(document.getElementById(getId("FromDate_MonthYear")));
			}
[#Com_DateFromMustStay] */
		}
	}

	this.MonthYearChanged = function(o/*MonthYear select*/) {
		var propName = o.name.replace(getId(""), "").split("_")[0], noOfDaysMinMax = getNoOfDaysMinMax(propName, o.options[o.selectedIndex].text), selectedDay = $(getIdJQ(propName + "_Day") + " option:selected").val();
		var minDay = noOfDaysMinMax[0], maxDay = noOfDaysMinMax[1];
		if ($(getIdJQ(propName + "_Day") + " option:first").val() != minDay || $(getIdJQ(propName + "_Day") + " option:last").val() != maxDay) {
			loadDates(propName, minDay, maxDay, selectedDay);
		}
		updateDatePicker(propName);
	}
	this.DayChanged = function(o/*Day select*/) {
		var propName = o.name.replace(getId(""), "").split("_")[0];
		updateDatePicker(propName);
	}
	this.OccupancyChanged = function(newValue) {
		if (!newValue) newValue = this.GetOccupancy();
		for (i = guestAgeShowIndex; i <= this.MaxOccupancy; i++) {
			var e = $('#trAge' + i + ', #trAge' + i + '_Mini'); if(newValue >= i) e.show(); else e.hide();
			if ($('form.xform').length == 0) if (i <= newValue) $(getIdJQ("GuestAgeList[" + (i - guestAgeShowIndex) + "]")).addClass('required'); else $(getIdJQ("GuestAgeList[" + (i - guestAgeShowIndex) + "]")).removeClass('required');
		}
		this.ValidateGuestAges();
		//$("#noOfGuests").text(newValue);
		//$("#totalGuests").text(parseInt(newValue) * parseInt($(getIdJQ("NoOfRooms")).val()));
	}
	this.GuestAgeChanged = function(i) {
		this.ValidateGuestAge(i);
	}

	this.ValidateGuestAges = function() {
		var occ = this.GetOccupancy()
		for (i = guestAgeShowIndex; i <= this.MaxOccupancy; i++)
			this.ValidateGuestAge(i);
	}
	this.ValidateGuestAge = function(i) {
		var isValid = true, occ = this.GetOccupancy();
		$('#trAge' + i + ' select:first, #trAge' + i + '_Mini select:first')
			.each(function() {
				var $this = $(this), $errMsg = $this.parent().children(".form-error-msg");
				$this.removeClass('invalid');
				$errMsg.hide();
				if (($this.val() == "" || $this.val() == "-1") && i <= occ) {
					$this.addClass('invalid');
					$errMsg.show();
					isValid = false;
				}
			});
		return isValid;
	}

	this.BeforeSubmit = function() {
		var isValid = $("form *.invalid").length == 0;
		return isValid;
	}
	

	/* private function */
	getIdJQ = function(propName) {
		return "#" + getId(propName);
	}
	getId = function(propName) {
		var prefix = (propName.indexOf("MonthYear") >= 0 || propName.indexOf("Day") >= 0) ? "" : ((avhForm ? avhForm.IdPrefix : idPrefix) + "_");
		return prefix + propName;
	}
	loadDates = function(propName, min, max, selectedDay) {
		$(getIdJQ(propName + "_Day")).removeOption(/./);
		for (i = min; i <= max; i++)
			$(getIdJQ(propName + "_Day")).addOption(i, (i < 10 ? "0" : "") + i);
		if (selectedDay)
			$(getIdJQ(propName + "_Day")).selectOptions(selectedDay, true);
	}
	getNoOfDaysMinMax = function(propName, monthYear) {
		var month = (monthYear.split(selectMYDSeparator)[0]),
			year = (monthYear.split(selectMYDSeparator)[1]),
			maxTotal = month < 8 ? (month % 2 == 0 ? (month == 2 ? (year % 4 == 0 ? 29 : 28) : 30) : 31) : (month % 2 == 0 ? 31 : 30),
			minDay = 1, maxDay = maxTotal;
		if (avhForm.MinDate.getMonth() + 1 == month && avhForm.MinDate.getFullYear() == year)
			minDay = avhForm.MinDate.getDate() + (propName == "ToDate" ? 1 : 0);
		if (avhForm.MaxDate.getMonth() + 1 == month && avhForm.MaxDate.getFullYear() == year)
			maxDay = avhForm.MinDate.getDate() + (propName == "FromDate" ? -1 : 0);
		return new Array(minDay, maxDay);
	}
	setDate = function(propName, date) {
		$(getIdJQ(propName + "_Day")).val(date.getDate());
		var month = date.getMonth() + 1, monthYearText = (month < 10 ? "0" : "") + month + selectMYDSeparator + date.getFullYear();
		var o = (document.getElementById(getId(propName + "_MonthYear")));
		for (i = 0; i < o.options.length; i++) {
			if (o.options[i].text == monthYearText) {
				o.selectedIndex = i;
				break;
			}
		}
		return o;
	}
	updateDatePicker = function(propName) {
		$(getIdJQ(propName)).val($.datepicker.formatDate(datePicker_DateFormat, avhForm.GetDateFromSelects(propName)));
	}

	/* init pri startu : */

	this.OccupancyChanged();
};/// <reference path="jquery-1.3-vsdoc.js" />
var regExp_CountryCity = /http(.?):\/\/(.+)\/(.+)\/(.*)\/?(.*)?\/?(.*)?\/?/;

$().ready(function() {

	if (typeof (mapCity) != "undefined") {
		mapCity.map.jmap('init', {
			'language': engine.LanguageCode,
			'mapCenter': mapCity.center,
			'mapZoom': 10,
			'mapControl': 'none',
			'mapEnableDragging': false,
			'mapEnableInfoWindows': false,
			'mapShowjMapsIcon': false
		});
	}

	if (typeof (cityOnChangeSelector) != "undefined") {
		$(cityOnChangeSelector).change(function() {
			var latitude, longitude, cityID, cityName, countryName;
			if (isNaN(this.value)) {
				var p = eval("(" + this.value + ")");
				cityID = p.CityID;
				cityName = p.Name;
				countryName = p.CountryName;
				latitude = p.Latitude;
				longitude = p.Longitude;
			} else {
				cityID = this.value;
				var e = geo_GetCityInfo(cityID, "en");
				if (e < 0) return;
				cityName = e.Name;
				countryName = e.CountryName;
				latitude = e.Latitude;
				longitude = e.Longitude;
			}

			$("#Weather_CityID, #BestRates_CityID").selectOptions(new RegExp('"CityID":' + cityID), true);
			$("#SelectedCity_CityID").selectOptions(cityID.toString(), true);

			$("#Weather_CityID, #BestRates_CityID, #SelectedCity_CityID, img.ajax-loading").toggle();
			engine.WeatherLoad(longitude, latitude);

			if ($("#containerBestRates").length > 0) {
				$.get("/" + engine.LanguageCode + "/WebService.mvc/bestrates", { 'cityID': cityID },
					function(data) {
						$("#containerBestRates").html(data);
						engine.ToolTipsInit($("#containerBestRates"));
						$("#BestRates_CityID").show();
						$("img.ajax-loading").hide();
						engine.VideoPlayerInit();
					});
			}
			var geoLink = "/" + countryName.toLowerCase() + "/" + cityName.toLowerCase();
			$("#weatherTitle").text(cityName);
			$("#mapCityLink").attr("href", "/" + engine.LanguageCode + "/hotels.mvc" + geoLink + "?map=on");

			if (typeof (mapCity) != "undefined" && mapCity.map.length > 0)
				Mapifies.MoveTo(mapCity.map, { 'mapCenter': [latitude, longitude] });

			$("#menu-lang a").each(function() {
				this.href = "/" + this.id.split("_")[1] + "/home.mvc" + geoLink;
			});
		}); //.change
	}
});


/*** Hotel List Map ***/
var mapHotelList = { 'map' : null, 'savedMarkerIndex' : null, 'savedHotelId' : null, 'arMarkers' : new Array(), timerID : null };
var MyMaps_CurrencyTop = null;
function mapHotelList_Show() {
	if ($("#containerHotelListMap").css("display") == "none")
		mapHotelList_Toggle();
} 
function mapHotelList_Toggle() {
	$("#containerHotelListMap, #hotels table.hotel .map-anchor").toggle();
	var isMapVisible = $("#containerHotelListMap").css("display") != "none";
	if (!isDef(mapHotelList.map))
		mapHotelList_Create();
	if (typeof(mapCity) != "undefined")
		Mapifies.MoveTo(mapHotelList.map, { 'mapCenter': mapCity.center });
	if (MyMaps_CurrencyTop == null) {
		var m = new RegExp("\\d+").exec(text = $("select.currency").css("top"));
		MyMaps_CurrencyTop = parseInt((m == null ? 0 : m[0]));
	}
	$("select.currency").css("top", (isMapVisible ? $("#containerHotelListMap").height() : 0) + MyMaps_CurrencyTop + "px");
	$("input[id$='_IsHotelsMapVisible']").val(isMapVisible);
	if (!isMapVisible && isDef(mapHotelList.savedHotelId))
		mapHotelList_SelectHotel(mapHotelList.savedHotelId, mapHotelList.savedMarkerIndex, false, false);
}

function mapHotelList_Destroy() {
	mapHotelList.map = null;
}
function mapHotelList_Create() {
	mapHotelList.map = $("#mapHotelList");
	if (mapHotelList.map.length < 1 || typeof(mapCity) == "undefined" || typeof(arHotelIDs) == "undefined")
		return;
	mapHotelList.map.jmap('init', {
		'language': engine.LanguageCode,
		'mapCenter': mapCity.center,
		'mapZoom': 11,
		'mapControl': 'large',
		'mapEnableType': true,
		'mapEnableDragging': true,
		'mapEnableInfoWindows': true,
		'mapEnableScaleControl': true,
		'mapShowjMapsIcon': false
	});

	$("table.hotel").each(function(i) {
		var hotelData = $(this).metadata();
		mapHotelList.map.jmap("AddMarker", {
				'pointIcon': MapHelper.GetHotelMarkerIcon(i, false),
				'pointLatLng': [hotelData.lat, hotelData.lng],
				'importance': 0
			}, function(marker, opt) {
			mapHotelList.arMarkers[hotelData.id] = marker;
			GEvent.addListener(marker, "click", function() { mapHotelList_SelectHotel(hotelData.id, i, true, false); });
		});
	});
}

function importanceOrder (marker,b) {
	return GOverlay.getZIndex(marker.getPoint().lat()) + marker.importance*1000000;
}

function mapHotelList_SelectHotel(hotelId, index, select, centerMap) {
	if (isDef(mapHotelList.savedHotelId)) { // -> unselect
		var tmlId = mapHotelList.savedHotelId; mapHotelList.savedHotelId = null;
		mapHotelList_SelectHotel(tmlId, mapHotelList.savedMarkerIndex, false, false);
		if (hotelId == tmlId)
			return;
		select = true;
	}
	var markerToRemove = mapHotelList.arMarkers[hotelId];
	var latLng = markerToRemove.getLatLng();
	Mapifies.RemoveMarker(mapHotelList.map, markerToRemove);
	mapHotelList.map.jmap("AddMarker", {
			'pointIcon': MapHelper.GetHotelMarkerIcon(index, select),
			'pointLatLng': [latLng.lat(), latLng.lng()],
			'importance': select ? 100 : 0,
			'zIndexProcess': function(marker, b) { return marker.importance; }
		}, function(marker, opt) {
			mapHotelList.arMarkers[hotelId] = marker;
			GEvent.addListener(marker, "click", function() { mapHotelList_SelectHotel(hotelId, index, !select, false); });
		});
	if (centerMap)
		Mapifies.MoveTo(mapHotelList.map, { 'mapCenter' : [latLng.lat(), latLng.lng()] });

	/* hotle minies */
	$("ul.hotel-minies li." + (select ? mapHotelList.savedHotelId : hotelId)).removeClass("active");
	if (select) {
		if ($("ul.hotel-minies li").length > 1)
			$("#hotel_" + hotelId).hide().insertAfter("#firstHotelContainer").addClass("active").fadeIn("slow");
		if (mapHotelList.timerID != null) clearTimeout(mapHotelList.timerID);
		mapHotelList.timerID = setTimeout("$('#hotel_" + hotelId + "').removeClass('active')", 3000);
		$("ul.hotel-minies li." + hotelId).addClass("active");
	}
	else
		$("#hotel_" + hotelId).removeClass("active");
	mapHotelList.savedHotelId = (select ? hotelId : null);
	mapHotelList.savedMarkerIndex = (select ? index : null);
}
