/**
 * jQuery.ScrollTo
 * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 5/25/2009
 *
 * @projectDescription Easy element scrolling using jQuery.
 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
 * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
 *
 * @author Ariel Flesler
 * @version 1.4.2
 *
 * @id jQuery.scrollTo
 * @id jQuery.fn.scrollTo
 * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
 *	  The different options for target are:
 *		- A number position (will be applied to all axes).
 *		- A string position ('44', '100px', '+=90', etc ) will be applied to all axes
 *		- A jQuery/DOM element ( logically, child of the element to scroll )
 *		- A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
 *		- A hash { top:x, left:y }, x and y can be any kind of number/string like above.
*		- A percentage of the container's dimension/s, for example: 50% to go to the middle.
 *		- The string 'max' for go-to-end. 
 * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
 * @param {Object,Function} settings Optional set of settings or the onAfter callback.
 *	 @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
 *	 @option {Number} duration The OVERALL length of the animation.
 *	 @option {String} easing The easing method for the animation.
 *	 @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
 *	 @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
 *	 @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
 *	 @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
 *	 @option {Function} onAfter Function to be called after the scrolling ends. 
 *	 @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
 * @return {jQuery} Returns the same jQuery object, for chaining.
 *
 * @desc Scroll to a fixed position
 * @example $('div').scrollTo( 340 );
 *
 * @desc Scroll relatively to the actual position
 * @example $('div').scrollTo( '+=340px', { axis:'y' } );
 *
 * @dec Scroll using a selector (relative to the scrolled element)
 * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
 *
 * @ Scroll to a DOM element (same for jQuery object)
 * @example var second_child = document.getElementById('container').firstChild.nextSibling;
 *			$('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
 *				alert('scrolled!!');																   
 *			}});
 *
 * @desc Scroll on both axes, to different values
 * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
 */
;(function( $ ){
	
	var $scrollTo = $.scrollTo = function( target, duration, settings ){
		$(window).scrollTo( target, duration, settings );
	};

	$scrollTo.defaults = {
		axis:'xy',
		duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1
	};

	// Returns the element that needs to be animated to scroll the window.
	// Kept for backwards compatibility (specially for localScroll & serialScroll)
	$scrollTo.window = function( scope ){
		return $(window)._scrollable();
	};

	// Hack, hack, hack :)
	// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
	$.fn._scrollable = function(){
		return this.map(function(){
			var elem = this,
				isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;

				if( !isWin )
					return elem;

			var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;
			
			return $.browser.safari || doc.compatMode == 'BackCompat' ?
				doc.body : 
				doc.documentElement;
		});
	};

	$.fn.scrollTo = function( target, duration, settings ){
		if( typeof duration == 'object' ){
			settings = duration;
			duration = 0;
		}
		if( typeof settings == 'function' )
			settings = { onAfter:settings };
			
		if( target == 'max' )
			target = 9e9;
			
		settings = $.extend( {}, $scrollTo.defaults, settings );
		// Speed is still recognized for backwards compatibility
		duration = duration || settings.speed || settings.duration;
		// Make sure the settings are given right
		settings.queue = settings.queue && settings.axis.length > 1;
		
		if( settings.queue )
			// Let's keep the overall duration
			duration /= 2;
		settings.offset = both( settings.offset );
		settings.over = both( settings.over );

		return this._scrollable().each(function(){
			var elem = this,
				$elem = $(elem),
				targ = target, toff, attr = {},
				win = $elem.is('html,body');

			switch( typeof targ ){
				// A number will pass the regex
				case 'number':
				case 'string':
					if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
						targ = both( targ );
						// We are done
						break;
					}
					// Relative selector, no break!
					targ = $(targ,this);
				case 'object':
					// DOMElement / jQuery
					if( targ.is || targ.style )
						// Get the real position of the target 
						toff = (targ = $(targ)).offset();
			}
			$.each( settings.axis.split(''), function( i, axis ){
				var Pos	= axis == 'x' ? 'Left' : 'Top',
					pos = Pos.toLowerCase(),
					key = 'scroll' + Pos,
					old = elem[key],
					max = $scrollTo.max(elem, axis);

				if( toff ){// jQuery / DOMElement
					attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );

					// If it's a dom element, reduce the margin
					if( settings.margin ){
						attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
						attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
					}
					
					attr[key] += settings.offset[pos] || 0;
					
					if( settings.over[pos] )
						// Scroll to a fraction of its width/height
						attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
				}else{ 
					var val = targ[pos];
					// Handle percentage values
					attr[key] = val.slice && val.slice(-1) == '%' ? 
						parseFloat(val) / 100 * max
						: val;
				}

				// Number or 'number'
				if( /^\d+$/.test(attr[key]) )
					// Check the limits
					attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );

				// Queueing axes
				if( !i && settings.queue ){
					// Don't waste time animating, if there's no need.
					if( old != attr[key] )
						// Intermediate animation
						animate( settings.onAfterFirst );
					// Don't animate this axis again in the next iteration.
					delete attr[key];
				}
			});

			animate( settings.onAfter );			

			function animate( callback ){
				$elem.animate( attr, duration, settings.easing, callback && function(){
					callback.call(this, target, settings);
				});
			};

		}).end();
	};
	
	// Max scrolling position, works on quirks mode
	// It only fails (not too badly) on IE, quirks mode.
	$scrollTo.max = function( elem, axis ){
		var Dim = axis == 'x' ? 'Width' : 'Height',
			scroll = 'scroll'+Dim;
		
		if( !$(elem).is('html,body') )
			return elem[scroll] - $(elem)[Dim.toLowerCase()]();
		
		var size = 'client' + Dim,
			html = elem.ownerDocument.documentElement,
			body = elem.ownerDocument.body;

		return Math.max( html[scroll], body[scroll] ) 
			 - Math.min( html[size]  , body[size]   );
			
	};

	function both( val ){
		return typeof val == 'object' ? val : { top:val, left:val };
	};

})( jQuery );


/*
 * jQuery Form Plugin
 * version: 2.36 (07-NOV-2009)
 * @requires jQuery v1.2.6 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($) {

/*
	Usage Note:
	-----------
	Do not use both ajaxSubmit and ajaxForm on the same form.  These
	functions are intended to be exclusive.  Use ajaxSubmit if you want
	to bind your own submit handler to the form.  For example,

	$(document).ready(function() {
		$('#myForm').bind('submit', function() {
			$(this).ajaxSubmit({
				target: '#output'
			});
			return false; // <-- important!
		});
	});

	Use ajaxForm when you want the plugin to manage all the event binding
	for you.  For example,

	$(document).ready(function() {
		$('#myForm').ajaxForm({
			target: '#output'
		});
	});

	When using ajaxForm, the ajaxSubmit function will be invoked for you
	at the appropriate time.
*/

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

	var url = $.trim(this.attr('action'));
	if (url) {
		// clean url (don't include hash vaue)
		url = (url.match(/^([^#]+)/)||[])[1];
   	}
   	url = url || window.location.href || '';

	options = $.extend({
		url:  url,
		type: this.attr('method') || 'GET',
		iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
	}, 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 = $('input:file', this).fieldValue();
	var found = false;
	for (var j=0; j < files.length; j++)
		if (files[j])
			found = true;

	var multipart = false;
//	var mp = 'multipart/form-data';
//	multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);

	// options.iframe allows user to force iframe mode
	// 06-NOV-09: now defaulting to iframe mode if file input is detected
   if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
	   // 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);
		var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

		var id = 'jqFormIO' + (new Date().getTime());
		var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" />');
		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', opts.iframeSrc); // 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 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 domCheckCount = 50;

		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;

				var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
				log('isXml='+isXml);
				if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
				 	if (--domCheckCount) {
						// in some browsers (Opera) the iframe DOM is not always traversable when
						// the onload callback fires, so we loop a bit to accommodate
						cbInvoked = 0;
						setTimeout(cb, 100);
						return;
					}
					log('Could not access iframe DOM after 50 tries.');
					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};
					return headers[header];
				};

				if (opts.dataType == 'json' || opts.dataType == 'script') {
					// see if user embedded response in textarea
					var ta = doc.getElementsByTagName('textarea')[0];
					if (ta)
						xhr.responseText = ta.value;
					else {
						// account for browsers injecting pre around json response
						var pre = doc.getElementsByTagName('pre')[0];
						if (pre)
							xhr.responseText = pre.innerHTML;
					}
				}
				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);
			}

			// 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;
	}).bind('click.form-plugin', function(e) {
		var target = e.target;
		var $el = $(target);
		if (!($el.is(":submit,input:image"))) {
			// is this a child element of the submit el?  (ex: a span within a button)
			var t = $el.closest(':submit');
			if (t.length == 0)
				return;
			target = t[0];
		}
		var form = this;
		form.clk = target;
		if (target.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 = $el.offset();
				form.clk_x = e.pageX - offset.left;
				form.clk_y = e.pageY - offset.top;
			} else {
				form.clk_x = e.pageX - target.offsetLeft;
				form.clk_y = e.pageY - target.offsetTop;
			}
		}
		// clear form vars
		setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
	});
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
	return this.unbind('submit.form-plugin click.form-plugin');
};

/**
 * 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, value: $(el).val()});
				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 it here
		var $input = $(form.clk), input = $input[0], n = input.name;
		if (n && !input.disabled && input.type == 'image') {
			a.push({name: n, value: $input.val()});
			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.  For example, consider the following form:
 *
 *  <form><fieldset>
 *	  <input name="A" type="text" />
 *	  <input name="A" type="text" />
 *	  <input name="B" type="checkbox" value="B1" />
 *	  <input name="B" type="checkbox" value="B2"/>
 *	  <input name="C" type="radio" value="C1" />
 *	  <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * 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);


/**
 * VerkehrsmittelVergleich.de
 *
 * Copyright VerkehrsmittelVergleich.de GmbH, 2009
 *
 * @filesource
 * @link             http://www.verkehrsmittelvergleich.de
 * @package          webapp
 * @subpackage       configuration
 * @author           Timo Derstappen
 */
(function($) {
    $.vmvConfig = {
        locale: 'de',

        /**
         * Widget selectors
         */
        selectors: {
            origin: '#vmv_search_job_raw_origin',
            destination: '#vmv_search_job_raw_destination',

            departure_date: '#vmv_search_job_raw_journey_date_date',

            departure_time: '#vmv_block_journey_date_time select',
            departure_radio: '#vmv_block_journey_date_type',

            return_block: '#vmv_block_departure_date_in',
            return_date: '#vmv_search_job_raw_departure_date_in',
            return_time: '#vmv_search_job_raw_departure_time_in',
            return_radio: '#vmv_block_departure_radio_in',

            extended_search: '#vmv_searching_ext',
            dates: 'div.date input',
            times: 'div.time select',
            suggest: 'input.suggest',
            suggest_list: '.search_suggest_wrapper',
            extended_link: 'div.advanced_search a.submit',
            is_round_trip: '#search_trip_in',
            transportations: 'fieldset.means input',
            transportation: 'fieldset.means',
            traveler_number: '#vmv_search_setting_traveler_number',
            traveler_setting: 'div.search_setting_traveler',
            traveler_age: 'div.search_setting_traveler input',
            train_card: 'div.search_setting_traveler select',
            car_type: '#vmv_search_setting_car_type_id',
            fuel: '#vmv_search_setting_car_fuel_id',
            consumption: '#vmv_search_setting_car_consumption',
            fuel_costs: '#vmv_search_setting_car_full_cost',
            notify: '#vmv_notify_wrapper'
        },

        config: {
          always_hide_departure_time: false
        },

        /**
         * Uris
         */
        uris: {
            origin: 'http://www.verkehrsmittelvergleich.de/stops/origin',
            destination: 'http://www.verkehrsmittelvergleich.de/stops/destination'
//            origin: 'http://192.168.1.68:3000/stops/origin',
//            destination: 'http://192.168.1.68:3000/stops/destination'
        },

        /**
         * Timeouts
         */
        timeouts: {
            suggest: 100
        },

        /**
         * Error messages
         */
        errors: {
            transportation: 'Bitte wählen Sie mindestens ein Verkehrsmittel',
            origin_empty: 'Bitte wählen Sie einen Abfahrtsort',
            destination_empty: 'Bitte wählen Sie einen Zielort aus',
            departure_date_empty: 'Bitte wählen Sie ein Abfahrtsdatum',
            return_date_empty: 'Bitte wählen Sie ein Rückfahrtsdatum',
            age_range: 'Bitte geben Sie Ihr Alter ein',
            date_invalid: 'Bitte wählen Sie ein Abfahrtsdatum',
            date_out_of_range: 'Es werden nur Daten innerhalb der nächsten 6 Monate unterstützt',
            date_no_train_prices: 'Bahnpreise sind nur für die nächsten 3 Monate verfügbar',
            departure_date_adjusted: 'Das Abfahrtsdatum wurde angepasst',
            return_date_adjusted: 'Das Rückfahrtsdatum wurde angepasst',
            wrong_inputs: "Bitte korrigieren Sie Ihre Angaben",
            today_message: "Ihre Zeitangabe ist sehr kurzfristig, bzw. liegt ggf. schon in der Vergangenheit, daher erhalten Sie unter Umständen keine Preisangabe."
        },

        /**
         * Defaults for form fields
         */
        defaults: {
            origin: 'z.B. München Hbf',
            destination: 'z.B. Berlin-Schönefeld Flughafen',
            return_date: '(demnächst verfügbar)',
            traveler_age: 35,
            date_offset: 4
        },

        /**
         * Time ranges
         */
        times: {
            early: '0:00 bis 11:00 Uhr',
            midday: '11:00 bis 16:00 Uhr',
            night: '16:00 bis 24:00 Uhr',
            all: '0:00 bis 24:00 Uhr'
        },

        journey_times: [
          '',
          '0:00 bis 11:00 Uhr',
          '11:00 bis 16:00 Uhr',
          '16:00 bis 24:00 Uhr',
          '0:00 bis 24:00 Uhr'
        ],

        /**
         * Predefined cars
         */
        cars: [
            {
                fuel: 1,
                consumption: 6.0,
                fuel_costs: 0.10
            },
            {
                fuel: 2,
                consumption: 5.0,
                fuel_costs: 0.10
            },
            {
                fuel: 3,
                consumption: 16.0,
                fuel_costs: 1.10
            }
        ],

        /**
         * Callbacks
         */
        callbacks: {
            /**
             * First entry in the suggest list is highlighted
             *
             * Scope (this) is the highlighted item
             *
             * @return void
             */
            suggestAutoHighlight: function() {
                if(typeof console !== 'undefined') {
                    console.log('suggest auto highlight');
                }
            },

            /**
             * An entry in the suggest list is highlighted
             *
             * Scope (this) is the highlighted item
             *
             * @return void
             */
            suggestTriggerHighlight: function() {
                if(typeof console !== 'undefined') {
                    console.log('suggest trigger highlight');
                }
            },

            /**
             * An entry in the suggest list was selected
             *
             * Scope (this) is the selected item
             *
             * @return void
             */
            suggestSelected: function() {
                if(typeof console !== 'undefined') {
                    console.log('suggest selected');
                }
            },

            suggestEmpty: function() {
              if(typeof console !== 'undefined') {
                console.log('suggest empty');
              }
            },

            /**
             * A new date is selected
             *
             * Scope (this) is the selected item
             *
             * @return void
             */
            dateSelected: function() {
                if(typeof console !== 'undefined') {
                    console.log('date selected');
                }
            },

            /**
             * A new time is selected
             *
             * Scope (this) is the selected item
             *
             * @return void
             */
            timeSelected: function() {
                if(typeof console !== 'undefined') {
                    console.log('time selected');
                }
            }
        }
    };

})(jQuery);


/**
 * VerkehrsmittelVergleich.de
 *
 * Copyright VerkehrsmittelVergleich.de GmbH, 2009
 *
 * @filesource
 * @link             http://www.verkehrsmittelvergleich.de
 * @package          webapp
 * @subpackage       startpage
 * @author           Timo Derstappen
 */

/**
 * Module pattern for better compatibility with other libraries
 *
 * @param object $ jQuery Namespace
 *
 * @return void
 */
(function($) {
    var debug = true; // Change this to true to start debugging

    /**
     * Debug logging (if enabled).
     * Requires Firebug or something similar that provides a console object
     *
     * @param string|array
     *
     * @return void
     */
    function log() {
        if (debug && typeof console !== 'undefined' && $.isFunction(console.log)) {
            console.log[console.firebug ? 'apply' : 'call'](
                console, Array.prototype.slice.call(arguments)
            );
        }
    };

    /**
     * Error logging (if debug is enabled).
     *
     * @param string|array
     *
     * @return void
     */
    function error() {
        if (debug) {
            if(typeof console !== 'undefined' && $.isFunction(console.log)) {
                console.error[console.firebug ? 'apply' : 'call'](
                    console, Array.prototype.slice.call(arguments)
                );
            } else {
                alert(arguments[0]);
            }
        }
    };

    /**
     * Debug object logging (if enabled).
     * Requires Firebug or something similar that provides a console object
     *
     * @param object
     *
     * @return void
     */
    function dir() {
        if (debug && typeof console !== 'undefined' && $.isFunction(console.log)) {
            console.dir[console.firebug ? 'apply' : 'call'](
                console, Array.prototype.slice.call(arguments)
            );
        }
    };

    /**
     * Vmv namespace
     *
     * This is the constructor with some checks if everything is in place
     */
     var VmvSearch = function() {
         if(typeof $.vmvConfig !== 'undefined') {
             this._config = $.vmvConfig;
             $.getScript('http://www.verkehrsmittelvergleich.de/javascripts/i18n/ui.datepicker-' + this._config.locale + '.js');
         } else {
             // defaults?
             error('Config not found');
         }
     }

     $.extend(VmvSearch.prototype, {
        /**
         * Configuration
         *
         * Contains translated messages and ranges. The Object is taken from the
         * global namespace, since the configuration should be generated
         * by the server independently.
         *
         * @var object
         */
        _config: null,

        /**
         * Get the configuration
         *
         * @param string key
         *
         * @return string|object|int
         */
        config: function(key) {
            var keys = key.split('.');
            value = this._config;

            if(keys) {
                $.each(keys, function() {
                    if(value && typeof value[this] !== 'undefined') {
                        value = value[this];
                    } else {
                        log('Configuration could not be found: ' + key);
                        value = false;
                    }
                });
            }

            return value;
        },

        /**
         * React on time changes in the form fields
         *
         * @param void
         */
        setTime: function() {
            // form scope
            var form = this.parents('form');
            var value = parseInt(this.val());
            var departure = this.is('#' + $.vmv.departure_time.attr('id'));

            var on_same_day = $.vmv.departure_date.val()
                === $.vmv.return_date.val();

            if(on_same_day) {
                if(departure) {
                    if(value != 4 && value > parseInt($.vmv.return_time.val())) {
                        // adjust arrival
                        $.vmv.return_time.val(value);
                        $.vmv.return_time.vmv('setTime');
                    }
                } else {
                    var departure_time = $.vmv.departure_time.val();
                    if(departure_time != 4 && value < parseInt(departure_time)) {
                        // adjust departure
                        $.vmv.departure_time.val(value);
                        $.vmv.departure_time.vmv('setTime');
                    }
                }
            }

            var time = $.vmv.config('times.early');
			if(departure) {
				$.vmv.departure_radio.show();
			} else {
				$.vmv.return_radio.show();
			}
            switch(value) {
                case 2:
                    time = $.vmv.config('times.midday');
                    break;
                case 3:
                    time = $.vmv.config('times.night');
                    break;
                case 4:
                    time = $.vmv.config('times.all');
                    if(departure) {
                    	$.vmv.departure_radio.hide();
                    } else {
                    	$.vmv.return_radio.hide();
                    }
                    break;
            }
            this.siblings('small').html(time);
        },

        /**
         * Initialize time form fields
         *
         * @return jQuery object
         */
        changeTime: function() {
          this.bind("change", function() {
            $(this).siblings("small").html( $.vmv.config('journey_times')[$(this).val()] );
            $(this).vmv('callback', 'timeSelected');
          });
        },

        /**
         * Toggle the extended search form
         *
         * @return void
         */
        extendedSearch: function() {
        	var element = this;
            //$.vmv.extended_search.hide();

            element.click(function() {
                if($.vmv.extended_search.get(0).style.display == 'none') {
                    $.vmv.extended_search.show();
                    element.addClass('open');
                } else {
                    $.vmv.extended_search.hide();
                    element.removeClass('open');
                }
                return false;
            });
        },

        /**
         * Initialize all selectors - scoped within one widget
         *
         * @return void
         */
        selectors: function() {
            var selectors = $.vmv.config('selectors');
            var form = this;

            $.each(selectors, function(key, value) {
                if(debug && !$(value, form).size()) {
                    log(key + ' selector not found: ' + value);
                }
                var selector = {};
                selector[key] = $(value, form);
                $.extend($.vmv, selector);
            });
        },

        /**
         * Initialize the search widget
         *
         * @return void
         */
        search: function() {
            // initialize selectors and form fields
            this.vmv('selectors');
            this.vmv('defaults');

            $.vmv.dates.vmv('initDate');
            $.vmv.suggest.vmv('addSuggest');
            $.vmv.times.vmv('changeTime');
            $.vmv.extended_link.vmv('extendedSearch');
            $.vmv.traveler_number.vmv('initTraveler');
            $.vmv.car_type.vmv('initCars');

            // round trip switch
            $.vmv.is_round_trip.change(function() {
                var element = $(this);

                if(element.is(':checked')) {
                    $.vmv.return_block.removeClass('disabled');
                    $.vmv.return_block.find('input').attr('disabled', '').val($.vmv.departure_date.val());
                    $.vmv.return_block.find('small').show();
                    $.vmv.return_block.siblings('div').show();
                    $.vmv.return_date.trigger('change');
                } else {
                    $.vmv.return_block.addClass('disabled');
                    $.vmv.return_block.find('input').attr('disabled', 'disabled').val($.vmv.config('defaults.return_date'));
                    $.vmv.return_block.find('small').hide();
                    $.vmv.return_block.siblings('div').hide();
                }
            });
            $.vmv.is_round_trip.trigger('change');
            $.vmv.is_round_trip.parents('#trip_in_disabled').find('h2').click(function() {
				      if($.vmv.is_round_trip.is(':checked')) {
					      $.vmv.is_round_trip.attr('checked', '');
				      } else {
					      $.vmv.is_round_trip.attr('checked', 'checked');
				      }
				      $.vmv.is_round_trip.trigger('change');
            });

            // remove error/success classes if the form changes
            this.find('input,select').change(function() {
//                $(this).removeClass('failed').removeClass('accepted');
//                $(this).parents('fieldset').removeClass('failed').removeClass('accepted');
            });

            // add tooltips
            // removed 6.1.2010 by Joe, wasn't working correctly

            this.submit(function() {
                var is_widget = $("#verkehrsmittelvergleich_de_widget").length > 0
                var form = $(this);
                form.vmv('clearErrors');

                if (is_widget) {
                  return true;
                }

                // mandatory fields
                form.vmv('validateNotEmpty', [
                    'origin',
                    'destination',
                    'departure_date'
                ]);

                // only validate the return date if return is selected
                if($.vmv.is_round_trip.val()) {
                    form.vmv('validateNotEmpty', ['return_date']);
                }

                // validate the defaults in the suggest boxes
                if($.vmv.origin.val() === "" || $.vmv.origin.val() === $.vmv.config('defaults.origin')) {
                    $.vmv.origin.vmv('addError', 'origin_empty');
                }
                if($.vmv.destination.val() === "" || $.vmv.destination.val() === $.vmv.config('defaults.destination')) {
                    $.vmv.destination.vmv('addError', 'destination_empty');
                }

                // transportation - at least one should be selected
                if(!$.vmv.transportations.is(':checked')) {
                    $.vmv.transportation.vmv('addError', 'transportation');
                }

                // passenger
                if(!($.data(form.get(0), 'errors').length < 1)) {
                    form.vmv('displayErrors');
                    return false;
                }
            });
        },

        /**
         * Car type selection
         *
         * @return void
         */
        initCars: function() {
            var element = this;
            var config = $.vmv.config('cars');

            element.change(function() {
                var value = element.val() - 1;

                $.vmv.fuel.val(config[value].fuel);
                $.vmv.consumption.val(config[value].consumption);
                $.vmv.fuel_costs.val(config[value].fuel_costs);
            });

            element.parents('fieldset').find('select,input')
                .not('#' + element.attr('id')).change(function() {
                    var custom = element.find('option:last').val();
                    element.val(custom);
                });

            element.trigger('change');
        },

        /**
         * Initialize date fields
         *
         * @return void
         */
        initDate: function() {
            var element = this;
            var first = true;

            if($('.hasDatepicker').size() > 0) {
                first = false;
            }

            element.datepicker({
                minDate: new Date(),
                maxDate: '+6m',
                defaultDate: $.vmv.config('defaults.date_offset'),
                // showButtonPanel: true,
                onChangeMonthYear: function(year, month) {
                    element.vmv('clearErrors');
                    element.vmv('validateMonth', year, month);
                },
                beforeShow: function(input) {
                  $("#ui-datepicker-div").addClass("verkehrsmittelvergleich_de_datepicker");
                },
                onClose: function(input) {
                  $("#ui-datepicker-div").delay(1000).removeClass("verkehrsmittelvergleich_de_datepicker");
                }
            });

            element.siblings('small').html( $.formatDate( element.datepicker('getDate'), "EEE, dd.MM.yyyy" ) );
            element.siblings(':hidden').val( element.datepicker('getDate') );

            element.siblings('span.calendar').click(function() {
                if(!element.is(':disabled')) {
                    element.datepicker( 'show' );
                }
            });

            element.change(function() {
                $.vmv.dates.vmv('clearErrors');
                var selected = element.vmvGet('getDate');
                element.siblings(':hidden').val(selected);
                if(selected) {
                    element.vmv('validateMonth',
                        selected.getFullYear(),
                        selected.getMonth()
                    );
                    element.siblings('small').html( $.formatDate( element.datepicker('getDate'), "EEE, dd.MM.yyyy" ) );
                    element.datepicker('option', {dateFormat: "dd.mm.y"});
                    element.vmv('callback', 'dateSelected');
                    var formated_today = $.formatDate( new Date(), "dd.MM.yyyy");
                    var formated_selection =  $.formatDate( element.datepicker('getDate'), "dd.MM.yyyy");
                    if (formated_today == formated_selection) {
                      $.vmv_notify( $.vmvConfig.errors.today_message );

                    }
                } else {
                    element.vmv('addError', 'date_invalid');
                    element.parents('form').vmv('displayErrors');
                }
            });

            if(first) {
                $('a.ui-datepicker-next.ui-state-disabled').live('click', function() {
                    element.vmv('clearErrors');
                    element.vmv('addError', 'date_out_of_range');
                    element.parents('form').vmv('displayErrors');
                });
            }
        },

        /**
         * Validate month
         *
         * @return void
         */
        validateMonth: function(year, month) {
            var today = new Date();
            if((month > today.getMonth() + 3) || (year > today.getFullYear() && (12 - month + today.getMonth() > 3))) {
                this.vmv('addError', 'date_no_train_prices');
                this.parents('form').vmv('displayErrors');
            }
        },

        /**
         * Get a date object from an date input field
         *
         * @return object
         */
        getDate: function() {
            var element = this;
            var date;
            var format = element.datepicker('option', 'dateFormat');
            var current = element.val();

            return $.datepicker.parseDate(format, current);
        },

        /**
         * Initialize the traveler
         *
         * @return void
         */
        initTraveler: function() {
            var element = this;

            element.change(function() {
                var number = parseInt(element.val());
                var fieldset = element.parents('fieldset');
                var setting = $.vmv.config('selectors.traveler_setting');

                $(setting + ':lt(' + number + ')', fieldset).show();
                $(setting + ':gt(' + number + '),' +
                    setting + ':eq(' + number + ')', fieldset).hide();
            });

            $.vmv.traveler_age.change(function() {
                var age = $(this);
                var value = parseInt(age.val());

                if(value < 0 || value > 131) {
                    var form = age.parents('form');
                    age.addClass('failed');
                    form.vmv('addError', 'age_range').vmv('displayErrors');
                    form.vmv('defaults', 'traveler_age');
                }
            });

            element.trigger('change');
        },

        /**
         * Set form defaults
         *
         * @return void
         */
        defaults: function(key) {
            if(typeof key !== 'undefined') {
                $.vmv[key].val($.vmv.config('defaults.' + key));
            } else {
                // all defaults
                $.each($.vmv.config('defaults'), function(key, value) {
                    if(typeof $.vmv[key] !== 'undefined') {
                        if (!$.vmv[key].val()) {
                          $.vmv[key].val(value);
                        }
                    }
                });
            }
        },

        /**
         * Validate form fields that are mandatory
         *
         * @param object fields List of fields with their errors
         *
         * @return void
         */
        validateNotEmpty: function(fields) {
            var form = this;
            $.each(fields, function(key, value) {
                if(!$.vmv[value].val()) {
                    $.vmv[value].vmv('addError', value + '_empty');
                }
            });
        },

	    /**
         * Add a suggest box
         *
         * @return void
         */
        addSuggest: function() {
            var type = this.is('#' + $.vmv.origin.attr('id')) ? 'origin' : 'destination';
            var element = this;
            var timeout;
            var default_text = $.vmv.config('defaults.' + type);
            var suggest_list = $.vmv.config('selectors.suggest_list');

            // disable form autocomplete
            element.attr('autocomplete' , 'off');

            element.focus(function() {
                if(element.val() === default_text) {
                    element.val('');
                    element.removeClass("default");
                }
            });

            element.keyup(function(event) {
                var key = parseInt(event.keyCode);
                var list = $(suggest_list, element.parent());

                switch(key) {
                    case 9:
                    case 13:
                        // enter
                        return false;
                    case 27:
                        // escape
                        list.trigger('close');
                        break;
                    case 38:
                        // key up
                        list.trigger('up');
                        break;
                    case 40:
                        // key down
                        list.trigger('down');
                        break;
                    default:
                        if(element.val().length > 1) {
                            if(timeout) {
                                window.clearTimeout(timeout);
                            }

                            timeout = window.setTimeout(function() {
                                element.vmv('getSuggest', type);
                            }, $.vmv.config('timeouts.suggest'));
                        }
                }
            });

            element.keydown(function(event) {
                element.removeClass("failed").removeClass("accepted");
                var list = $(suggest_list, element.parent());
                var key = parseInt(event.keyCode);
                if(!event.shiftKey && (key === 13 || key === 9)) {
                    if(list.size() > 0) {
                        list.trigger('select');
                        return false;
                    }
                }
            });

            element.blur(function() {
                if(!element.val()) {
                    element.val(default_text);
                    element.addClass("default");
                }
            });

          // remove all visible suggest lists if click is not inside of them
          $(document).mousedown(function(event) {
            var target = $(event.target);
            if(target.parents(suggest_list).size() === 0 && !target.is('#' + element.attr('id')) && !target.hasClass("suggest_list")) {
              $(suggest_list + ":visible").remove();
              $.vmv.departure_time.show();
            }
          });
        },

        /**

         * Get suggest data
         *
         * @param string type Either origin or destination
         *
         * @return void
         */
        getSuggest: function(type) {

          var element = this;
          var suggest_list = $.vmv.config('selectors.suggest_list');
          var uri = $.vmv.config('uris.' + type);
          element.addClass('loading');
          $.getJSON(uri + '?q=' + escape(element.val()) + '&callback=?', function(response) {
            element.removeClass('loading');

            if (typeof response.data !== 'undefined' && $(response.data).size() > 0) {
              // remove old, add new suggest list
              $(suggest_list).remove();
              element.after(response.content);

              var list = $(suggest_list, element.parent());
              $.data(list.get(0), 'locations', response.data);

              $('li', list).removeClass('selected');
              list.vmv('bindSuggest', type);


            } else if (typeof response.content !== 'undefined' && $(response.content).size() > 0) {
              $(suggest_list).remove();
              element.after(response.content);
              var list = $(suggest_list, element.parent());
              element.addClass('failed');
              list.vmv('bindSuggest', type);
            }
          });
        },

        /**
         * Bind suggest events
         *
         * @param string type
         *
         * @return void
         */
        bindSuggest: function(type) {
            var list = this;
            var element = $.vmv[type];

            // hide time select boxes for ie6
            if (type == "origin") {
              $.vmv.departure_time.hide();
            } else {
              if ($.vmv.config('config.always_hide_departure_time')) {
                $.vmv.departure_time.hide();
              }
            }

            // close the suggest list
            list.bind("close", function() {
                $(this).remove();

                // show time select boxes
                $.vmv.departure_time.show();
                $.vmv.return_time.show();
            });

            // move up in the suggest list
            list.bind("up", function() {
                var selected = $('li.selected', list);
                var previous_index = $(".suggest li").index( selected ) - 1;
                var li = $(".suggest li:eq("+previous_index+")");
                selected.removeClass('selected');
                li.trigger('highlight');
            });

            // move down in the suggest list
            list.bind("down", function() {
                var selected = $('li.selected', list);
                var next_index = $(".suggest li").index( $('li.selected') ) + 1;
                var li = $(".suggest li:eq("+next_index+")");
                selected.removeClass('selected');
                li.trigger('highlight');
            });

            // scroll list to the selected item
            list.find('li').bind('highlight', function(event, auto) {
              var li = $(this);
              li.addClass('selected');
              $(".suggest_list").scrollTo(li, 0, {offset: -100});

              if(typeof auto !== 'undefined' && auto === true) {
                li.vmv('callback', 'suggestAutoHighlight');
              } else {
                li.vmv('callback', 'suggestTriggerHighlight');
              }
              return false;
            });

            // select an item in the suggest list
            list.bind('select', function() {
                $(this).find('li.selected').click();
            });

            // click on an item in the suggest list
            $('li', list).click(function() {


                var li = $(this);
                li.vmv('showPin', type);

                var selected = li.clone();
                selected.find('ul').remove();
                element.val($.trim(selected.text()));
                element.addClass('accepted');
                element.vmv('clearErrors');

                li.vmv('callback', 'suggestSelected');
                list.trigger('close');

                if(type === 'origin') {
                    $.vmv.destination.focus();
                } else {
                    $.vmv.departure_date.focus();
                }

                return false;
            });

            $('li:first', list).trigger('highlight', [true]);

            if ($(".search_suggest_wrapper ul:empty").size() > 0) {
              element.addClass("failed");
              element.removeClass("accepted");
              $(this).vmv('callback', 'suggestEmpty');
            }

        },

        /**
         * Show the pin of the selected location on the map
         *
         * @return void
         */
        showPin: function(type) {
            var li = $(this);
            var id = li.attr('stop_id');
            var list = li.parents($.vmv.config('selectors.suggest_list'));
            var data = $.data(list.get(0), 'locations');

            if(typeof data['suggest_location_'+id] !== 'undefined' && typeof data['suggest_location_'+id].coords !== 'undefined') {
                if($('#pin_' + type).size() === 0) {
                    $('#map').append('<div id="pin_' + type + '"></div>');
                }

                $('#pin_' + type).css({
                    left: data['suggest_location_'+id].coords[0] + 'px',
                    top: data['suggest_location_'+id].coords[1] + 'px'
                });
            }
        },

        /**
         * Add an error
         *
         * @param string error
         *
         * @return void
         */
        addError: function(error) {
            var form = this.parents('form');
            var element = this;
            var errors = $.makeArray($.data(form.get(0), 'errors'));
            errors.push({
                field: element,
                error: error
            });

            $.data(form.get(0), 'errors', errors);
        },

        /**
         * Display errors
         *
         * @return void
         */
        displayErrors: function() {

            // Hijack notify ok event.
            $.vmv.notify.find('.ok').click(function() {
              $.vmv.notify.hide();
              return false;
            });

            if($.vmv.notify.size() === 0) {
                // initialize notification box
                $.vmv.notify = $($.vmv.config('selectors.notify'));
                console.log($.vmv.notify);
            }

            var errors = '';
            $.each($.data(this.get(0), 'errors'), function(key, value) {
                var error = $.vmv.config('errors.' + value.error);
                log('errors.' + value.error + ': ' + error);
                if( value.error != 'transportation' ){
                  value.field.addClass('failed');
                }
                errors += '<li>' + error + '</li>';
            });
            if(errors !== '') {
                errors = '<ul>' + errors + '</ul>';
                $.vmv.notify.show().find('div.notify_content').html(errors);
            } else {
                $.vmv.notify.hide();
            }
        },

        /**
         * Clear all errors
         *
         * Remove the displayed errors and clean up the error object
         *
         * @param string error
         *
         * @return void
         */
        clearErrors: function() {
            var form = this;
            var errors = new Array;

            if(!form.is('form')) {
                // clear single error
                var element = this;
                form = element.parents('form');
                var data = $.data(form.get(0), 'errors');
                if(data) {
                    $.each(data, function(key, value) {
                        if(element.attr('id') !== value.field.attr('id')) {
                            errors.push(value);
                        }
                    });
                }
            }
            $.data(form.get(0), 'errors', errors);
            form.vmv('displayErrors');
        },

        /**
         * Trigger a callback
         *
         * @param string callback
         *
         * @return
         */
        callback: function(callback) {
            callback = $.vmv.config('callbacks.' + callback);

            if($.isFunction(callback)) {
                callback.apply(this);
            }
        }

    });

    /**
     * Invoke the vmv functionality.
     *
     * @return  jQuery object
     */
    $.fn.vmv = function(method) {
        if(typeof $.vmv[method] !== 'undefined') {
            var args = $.makeArray(arguments);
            args.shift();
            return this.each(function() {
                $.vmv[method].apply($(this), args);
            });
        } else {
            log('Method not found: ' + method);
        }
    };

    /**
     * Retrieve vmv data
     *
     * @return mixed
     */
    $.fn.vmvGet = function(method) {
        if(typeof $.vmv[method] !== 'undefined') {
            var args = $.makeArray(arguments);
            args.shift();
            return $.vmv[method].apply($(this), args);
        } else {
            log('Method not found: ' + method);
        }
    };

    $(function() {
        $.vmv = new VmvSearch(); // singleton instance
    });

    if( typeof vmv_set_widget_configs == "function" ){
      vmv_set_widget_configs();
    }

})(jQuery);





/* News flashing */
(function($) {


  $.vmv_news = {

    current: 0,
    data: [ ],

    change: function() {
      var current = $.vmv_news.current;
      var data = $.vmv_news.data;

      $("#news").html(data[current]);

//      $("#news").fadeOut("slow", function() {
//        $("#news").html(data[current]).fadeIn("slow");
//      });

      if (data.length > 1) {

        setTimeout($.vmv_news.change, 5000);

        if (current >= data.length-1) {
          $.vmv_news.current = 0;
        } else {
          $.vmv_news.current++;
        }

      }
    }

  };

})(jQuery);


(function($) {
  $.vmv_dialog = function(title, msg) {
    $("#dialog").html(msg);
    $("#dialog").dialog({
      bgiframe:  true,
      resizable: false,
      autoOpen:  true,
      height:    180,
      width:     350,
      modal:     true,
      overlay:   { backgroundColor: '#000', opacity: 0.5 },
      title:     title,
      buttons:   { 'Ok': function() { $(this).dialog('close'); } }
    });
  }
})(jQuery);



(function($) {
  $.vmv_notify = function(msg) {
    var content = "<ul>";
    if (msg.constructor == Array) {
      $.each(msg, function() {
        content += "<li>" + this + "</li>";
      });
    } else {
      content += "<li>" + msg + "</li>";
    }
    $("#vmv_notify_wrapper .notify_content").html( content );
    $("#vmv_notify_wrapper").show();
    $('#vmv_notify .ok').click(function() { $.vmv.notify.hide(); return false; });
  }
})(jQuery);





// jQuery in action
(function($){
  $.formatDate = function(date,pattern) {
    var result = [];
    while (pattern.length>0) {
      $.formatDate.patternParts.lastIndex = 0;
      var matched = $.formatDate.patternParts.exec(pattern);
      if (matched) {
        result.push($.formatDate.patternValue[matched[0]].call(this,date));
        pattern = pattern.slice(matched[0].length);
      }
      else {
        result.push(pattern.charAt(0));
        pattern = pattern.slice(1);
      }
    }
    return result.join('');
  };

  $.formatDate.patternParts =
    /^(yy(yy)?|M(M(M(M)?)?)?|d(d)?|EEE(E)?|a|H(H)?|h(h)?|m(m)?|s(s)?|S)/;

  $.formatDate.monthNames = [
    'January','February','March','April','May','June','July',
    'August','September','October','November','December'
  ];

  $.formatDate.dayNames = [
    'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday',
    'Saturday'
  ];

  $.formatDate.dayNamesAbbr = ['So','Mo','Di','Mi','Do','Fr','Sa'];


  $.formatDate.patternValue = {
    yy: function(date) {
      return $.toFixedWidth(date.getFullYear(),2);
    },
    yyyy: function(date) {
      return date.getFullYear().toString();
    },
    MMMM: function(date) {
      return $.formatDate.monthNames[date.getMonth()];
    },
    MMM: function(date) {
      return $.formatDate.monthNames[date.getMonth()].substr(0,3);
    },
    MM: function(date) {
      return $.toFixedWidth(date.getMonth()+1,2);
    },
    M: function(date) {
      return date.getMonth()+1;
    },
    dd: function(date) {
      return $.toFixedWidth(date.getDate(),2);
    },
    d: function(date) {
      return date.getDate();
    },
    EEEE: function(date) {
      return $.formatDate.dayNames[date.getDay()];
    },
    EEE: function(date) {
      return $.formatDate.dayNamesAbbr[date.getDay()];
//      return $.formatDate.dayNames[date.getDay()].substr(0,3);
    },
    HH: function(date) {
      return $.toFixedWidth(date.getHours(),2);
    },
    H: function(date) {
      return date.getHours();
    },
    hh: function(date) {
      var hours = date.getHours();
      return $.toFixedWidth(hours>12 ? hours - 12 : hours,2);
    },
    h: function(date) {
      return date.getHours()%12;
    },
    mm: function(date) {
      return $.toFixedWidth(date.getMinutes(),2);
    },
    m: function(date) {
      return date.getMinutes();
    },
    ss: function(date) {
      return $.toFixedWidth(date.getSeconds(),2);
    },
    s: function(date) {
      return date.getSeconds();
    },
    S: function(date) {
      return $.toFixedWidth(date.getMilliseconds(),3);
    },
    a: function(date) {
      return date.getHours() < 12 ? 'AM' : 'PM';
    }
  };

  $.toFixedWidth = function(value,length,fill) {
    if (!fill) fill = '0';
    var result = value.toString();
    var padding = length - result.length;
    if (padding < 0) {
      result = result.substr(-padding);
    }
    else {
      for (var n = 0; n < padding; n++) result = fill + result;
    }
    return result;
  };

})(jQuery);


/**
 * VerkehrsmittelVergleich.de
 *
 * Copyright VerkehrsmittelVergleich.de GmbH, 2009
 *
 * @filesource
 * @link             http://www.verkehrsmittelvergleich.de
 * @package          webapp
 * @subpackage       startpage
 * @author           Timo Derstappen
 */

/**
 * Module pattern for better compatibility with other libraries
 *
 * @param object $ jQuery Namespace
 *
 * @return void
 */
(function($) {
    /* Document ready */
    $(function(){
        /* initialize the search widget */
        $('#vmv_search_form form').vmv('search');

        /* capture tooltips */
        $('a.tooltip').live("mouseover", function() {
          $(this).next('div.tooltip').show();
        });
        $('a.tooltip').live("mouseout", function() {
          $(this).next('div.tooltip').hide();
        });

        /* capture ajax events */
        $(".ajax_link").live("click", function () {
          $.getScript(this.href);
          return false;
        });

        //$("#feedback_content").animate({width:'hide'});
        $("#feedback_button").click().toggle(
          function() { $("#feedback_content").animate({width:'hide'}) },
          function() { $("#feedback_content").animate({width:'show'}) }
        );

    });
})(jQuery);




/* Filter and result list handling */
(function($) {

  $.vmv_result = {

    is_widget: $("#verkehrsmittelvergleich_de_widget").size() > 0,

    init: function() {
      this.init_form();
      this.capture_sorting_events();
      this.capture_pagination_events();
      this.capture_toggle_details_events();
    },

    init_form: function() {
      $('#filter_form').ajaxForm( {
        dataType: 'script',
        beforeSubmit: function() { $('.content_box_table_top .status').show(); },
        success:  function(response) { $('.content_box_table_top .status').hide(); $('#trip_list').html(response); },
        error: function(xhr, ajaxOptions, thrownError) { $('.content_box_table_top .status').hide(); $('#trip_list').html(xhr.responseText); }
      });
    },

    init_slider: function(selector, min_value, max_value) {
      $(selector).slider({
        range: "min", min: min_value, max: max_value, value: max_value
      });
    },

    init_range_slider: function(selector, min_value, max_value) {
      $(selector).slider({
        range: true, min: min_value, max: max_value, values: [min_value, max_value]
      });
      $(selector + " a:first").addClass("first");
    },

    duration: function(seconds) {
      var h = parseInt(seconds/3600);
      var m = parseInt((seconds-(h*3600))/60);
      return h+":"+this.add_leading_zero(m);
    },

    add_leading_zero: function(value) {
      return String(value).length<2 ? "0"+value : value;
    },

    get_formated_time: function(date) {
      return this.add_leading_zero(date.getUTCHours()) + ":" + this.add_leading_zero(date.getUTCMinutes());
    },

    to_utc: function(date) {
      return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds() );
    },

    /* filter set to 1 and submit may be a new function */
    bind_slide_events: function() {

      $('.ui-slider').bind('slidechange', function(event, ui) {
        var type = /slider_([a-z]*)(_at)?/.exec( $(this).attr("id") )[1];
        $.vmv_result.perform_action(type, ui);
        $("#filter_page").attr("value", 1);
        $("#filter_form").submit();
      });

      $('.ui-slider').bind('slide', function(event, ui) {
        var type = /slider_([a-z]*)(_at)?/.exec( $(this).attr("id") )[1];
        $.vmv_result.perform_action(type, ui);
      });
    },

    perform_action: function( type, ui ) {
      switch (type) {
        case "duration":
            $("#filter_duration_max").val(ui.value);
            $("#duration_end").val( this.duration(ui.value) );
            break;
        case "price":
            $("#price_end").val( Math.round(ui.value) );
            break;
        case "transfer":
            $("#transfer_end").val(ui.value);
            break;
        case "co":
            $("#filter_co2_max").val(ui.value);
            break;
        case "departure":
            this.slider_changes_date(type, ui.values[0], ui.values[1]);
            break;
        case "arrival":
            this.slider_changes_date(type, ui.values[0], ui.values[1]);
            $("#arrival_day_min").html( $.formatDate( new Date(ui.values[0]*1000), "EEE" ) );
            $("#arrival_day_max").html( $.formatDate( new Date(ui.values[1]*1000), "EEE" ) );
            break;
      }
    },

    slider_changes_date: function(key, time_min, time_max) {
      var min_date = new Date(time_min*1000);
      var max_date = new Date(time_max*1000);
      $("#filter_"+key+"_at_min").val( time_min );
      $("#filter_"+key+"_at_max").val( time_max );
      $("#"+key+"_time_start").val( this.get_formated_time(min_date) );
      $("#"+key+"_time_end").val(   this.get_formated_time(max_date) );
    },

    /* SORTING */
    capture_sorting_events: function() {
      $("a.sort_link").live("click", function () {
        $("#filter_sort").attr("name",  $(this).attr("name") );
        $("#filter_sort").attr("value", $(this).attr("value") );
        $("#filter_page").attr("value", 1);
        $("#filter_form").submit();
        return false;
      });
    },

    /* PAGINATION */
    capture_pagination_events: function() {
      $(".pagination a").live("click", function() {
        $("#filter_page").attr("value", $(this).attr("value"));
        $("#filter_form").submit();
        return false;
      });
    },

    /* TOGGLE DETAILS */
    capture_toggle_details_events: function() {
      $("a.toggle_details, .closed td.departure_at, .closed td.arrival_at, .closed td.via, .closed td.transfer, .closed td.duration").live("click", function() {
        var row = $(this).closest('tr');
        row.toggleClass('opened');
        row.next().toggle();
        row.next().next().toggle();
        return false;
      });
    },

    /* SERIALIZATION */
    get_search_job_variables: function() {
      var opened = new Array;
      jQuery.each( $('#routes tr.opened'), function() {
        var id = $(this).attr('id').match(/\d+/);
        opened.push( id[0] );
      });
      return $('#filter_form').serialize() + "&opened=" + opened.join(",");
    }

  };

})(jQuery);


/*
 * Progress extension.
 */

(function($) {


  $.vmv_progress = {

    /* Defaults for progress process controlling */
    timeout_intervall: 40,
    redirection_path: "path",
    query_state_path: "",
    failed_redirection_path: "/",
    state_request_counter: 0,
    redirection_status: false,
    progressbar_increase_value: 1,
    state_check_intervall: 2,
    step_content_values: [[ ],[ ],[ ]],
    current_step: 0,
    current_step_value: 0,
    search_job_id: 0,
    debug: false,

    /* Start the progress bar run, search job observation and the animations. */
    start: function() {
      $('#progress_bar').progressbar({ value: 1 });
      $.vmv_progress.update_progress();
      $.vmv_progress.update_step_content();
      setTimeout( $.vmv_progress.search_job_failed, 70*1000 );
    },

    /* Submit ajax calls to the application. */
    watch_search_job_state: function() {
      $.ajax({
        type: 'get',
        url: $.vmv_progress.query_state_path,
        dataType: 'script',
        cache: false,
        success: function(data, textStatus) {
          $.vmv_progress.redirection_status = true;
          $.vmv_progress.speedup_progress();
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
          setTimeout($.vmv_progress.watch_search_job_state, ($.vmv_progress.state_check_intervall*1000));
        }
      });
    },

    speedup_progress: function() {
      $.vmv_progress.timeout_intervall = 6;
      $.vmv_progress.progressbar_increase_value = 3;
    },

    update_progress: function() {
      var progress = parseInt($('#progress_bar').attr('aria-valuenow'));

      if (progress < 100) {
        $("#progress_bar").progressbar("value", progress + $.vmv_progress.progressbar_increase_value);
        var step = 1;

        if (progress < 40) {
          step = 1;
        } else if (progress < 70) {
          step = 2;
        } else if (progress < 85) {
          step = 3;
        } else {
          step = 4;
        }

        if ( step != $.vmv_progress.current_step ) {
          $.vmv_progress.current_step = step;
          $.vmv_progress.stop_progress_step( $.vmv_progress.current_step-1 );
          $.vmv_progress.start_progress_step( $.vmv_progress.current_step );
        }

        setTimeout( $.vmv_progress.update_progress, $.vmv_progress.timeout_intervall*10 );

      } else {
        if ($.vmv_progress.redirection_status) {
          $.vmv_progress.stop_progress_step( $.vmv_progress.current_step );
 //         if (!$.vmv_progress.debug) {
            window.location.replace($.vmv_progress.redirection_path);
 //         }
        } else {
          setTimeout( $.vmv_progress.update_progress, 1000 );
        }
      }

    },


    start_progress_step: function(number) {
      $('#progress_step' + number).addClass('on');
      $('#progress_step' + number).removeClass('off');
    },


    stop_progress_step: function(number) {
      $.vmv_progress.current_step_value = 0;
      $('#progress_step' + number).addClass("done");
      $("#step" + number + "_content").html("");
    },


    update_step_content: function() {
      var current = $.vmv_progress.current_step;
      var content_key = current-1;
      var content = $.vmv_progress.step_content_values[content_key];
      var content_value = $.vmv_progress.current_step_value;
      var current_length = 2.5;
      if (current == 2) {
        current_length = 3.1;
      }

      if (current < 3) {

        setTimeout($.vmv_progress.update_step_content, $.vmv_progress.timeout_intervall/(current_length * content.length) * 1000);
        if(  typeof content[ content_value ] !== 'undefined' ){
          $("#step" + current + "_content").html( content[ content_value ][ 'name' ]);
          $("#step" + current + "_content").css('color',  content[ content_value ][ 'color' ]);
        }
        $.vmv_progress.current_step_value++;
      }
    },

    search_job_failed: function() {
      if ($.vmv_progress.redirection_status) {
        window.location.replace($.vmv_progress.redirection_path);
        $.ajax({
          type: 'get',
          url: $.vmv_progress.search_job_id + '/failed?code=redirect',
          dataType: 'html'
        });
      } else {
        $.ajax({
          type: 'get',
          url: $.vmv_progress.search_job_id + '/failed?code=failed',
          dataType: 'html'
        });
        window.location.replace($.vmv_progress.failed_redirection_path + "?e=redirect");
      }
    }
  };

  $.extend($.vmv_progress);

})(jQuery);
