
function handleForms($forms , settings) {
	var submitTooltipTmr;
	$forms = $($forms);

	// modules vars are optional
	var modules = {
		// display the fields label as the fields value?
		minimal:{},
		// use tooltip?
		tooltip:{
			tipPosition:		'left top',		// tip's position compared to it's parent, ie. 'center bottom'
			parentPosition:		'right top',		// tip's parent's position compared to tip, ie. 'center top'
			offset:			'0 0'			// ie. '0 10' (x/y offset)
		},
		// use ajax?
		ajax:{
			script:			'/mail.php'		// ajax script's pathname

			// return	nothing/null/undefined to alert standard message
			// return	string (message/settings.texts id) for custom alert
			// return	FALSE to cancel alert
//			complete: function($form , formData , message , returnObject) {}
		},
		mail:{
			// mail vars may can be strings or functions
			subject:		'Contact through website',
			prependix:		'',
			appendix:		''
		},
		reset_form_on_complete:true, // reset form after [default: true]
		dummy:1
	}

	// default vars always used
	var defaults = {
		// some alert texts
		texts:{
			requiredField:		'Required field',
			invalidToAddress:	'Incorrect e-mailaddress',
			invalidEmailaddress:	'Incorrect e-mailaddress',
			incompleteForm:		'Please fill out the form correctly',
			dataSent:		'Thank you, your data has been sent!',
			connectionError:	'ERROR: cannot connect to serverside mailscript (ajax)'
		},
		funcs:{
			// return	FALSE/0/'' to cancel form.submit
			// return	nothing/null/undefined or any TRUE variable to continue form.submit
//			complete: function($form , formData) {},

			// return	string (message/settings.texts id) on error
			// return	nothing/null/undefined to continue
			// NB: TRUE or FALSE are not allowed (see function for explanation)
//			checkField: function($elm , elm , name , value) {},

			// return	string (message/settings.texts id) on error
			// return	nothing/null/undefined to continue
//			makeHtmlValue: function($elm , elm , name , value) {}	// return string
		}
	};

	// loop through different sets of default settings
	if (typeof(settings)!=='object') settings={};
	else {
		// 1) modules
		for (var name in modules) {
			if (settings[name]) {
				if (typeof(settings[name])!=='object')	settings[name] = modules[name];
				else 					settings[name] = $.extend(modules[name] , settings[name]);
			}
		}
	}

	// 2) defaults
	for (var name in defaults) {
		if (typeof(settings[name])!=='object')	settings[name] = defaults[name];
		else					settings[name] = $.extend(defaults[name] , settings[name]);
	}

//alert(settings.tooltip.tipPosition)
//alert(settings.tooltip.offset); return;

	// loop forms
	$($forms).each(function(i,form) {
		var $form		= $(form);
		var $textFields		= $form.find('input , textarea').not('[type=submit] , [type=button] , [type=hidden]');
		var $submitButton	= $form.find('input[type=submit]').test();

		// loop all form elements to prepare them
		$textFields.each(function(i,elm) {
			if (elm.title) {
				var $elm = $(elm);

				// add field for label for serverside purposes
				$elm.after($("<input type='hidden' name='_label_" + elm.name + "' />").val(elm.title.replace(/ \*$/ , '')));

				// add field for HTML'd value for serverside purposes
				$elm.after($("<input type='hidden' name='_html_" + elm.name + "' />"));

				// if minimal
				if (settings.minimal) {
					$elm.bind({
						focus:	function(e) {if (elm.value===elm.title)	{elm.value=''; $elm.removeClass('empty'); }},
						blur:	function(e) {if (!elm.value)		{$elm.addClass('empty'); elm.value=elm.title; }}
					}).trigger('blur');
				}
			}
		});

		// create formfields for mail vars
		if (settings.mail) for (var name in settings.mail) $form.prepend($("<input type='hidden' name='_mail_" + name + "' />"));

		// bind submit event to form
		$form.bind('submit.handleForms' , function() {
			// manually blur probable focussed fields (did not automatically happen in FF3.6)
			if (settings.minimal) $form.find(':focus').trigger('blur');

			// check all fields
			var errors=[];
			$textFields.each(function(i,elm) {
				var $elm = $(elm);
				if (t=checkField(elm)) errors.push(t);
				makeHtmlValue(elm);
			});

			// if errors
			if (errors.length>0) {
				// if tooltip
				if (settings.tooltip) {
					// if submitbutton and standard form incomplete text (not needed for ajax forms)
					if ($submitButton && settings.texts.incompleteForm) {
						var message = settings.texts.incompleteForm;
						$submitButton.tooltip('show' , message , settings.tooltip);
						clearTimeout(submitTooltipTmr);
						submitTooltipTmr = setTimeout(function() {$submitButton.tooltip(); } , 2000);
					}
				}
				else {
					// if no standard form incomplete text (needed for non-ajax forms)
					if (!settings.texts.incompleteForm) alert('ERROR: please set texts.incompleteForm')
					else {
						// create error message
						var message = [settings.texts.incompleteForm];
						for (var i=0; i<errors.length; i++) message.push(errors[i].$elm.attr('title').replace(/ \*$/ , '') + ': ' + errors[i].message);

						// alert message and focus on field afterwards
						alert(message.join('\n - '));
					}
				}

				// focus on 1st erroneous formfield
				errors[0].$elm.trigger('focus').trigger('select');

				return false;
			}
			// if no errors
			else {
				// put settings.mail vars in formfields
				for (var name in settings.mail) {
					var t = settings.mail[name];
					$form[0].elements['_mail_'+name].value = typeof(t)=='function' ? t():t;
				}

				// serialize form data
				var formData = $form.serializeObject();

				// call custom complete event, cancel form.submit if return is false/0/''
				if (settings.funcs.complete) {
					var t = settings.funcs.complete($form , formData);
					if (t===false || t===0 || t==='') return false;
				}

				// if ajax: collect vars, send to server and return false so form.submit is cancelled
				if (settings.ajax) {
					$.ajax({url:settings.ajax.script , data:formData , type:'POST' , dataType:'JSON'})
						.success(function(ret) {
							var message; // can be actual message or settings.texts id

							// if ret.error is defined in settings.texts
							if (ret.error) message = ret.error;
							else {
								// reset form
								if (settings.reset_form_on_complete===null || settings.reset_form_on_complete) $form.trigger('reset');
								if (settings.tooltip) $textFields.tooltip();
								if (settings.minimal) $textFields.blur();
								message = 'dataSent';
							}

							// get message from 
							if (settings.ajax.complete) {
//								var ret	= settings.ajax.complete($form , formData , settings.texts[message] || message , ret.error);
								var ret	= settings.ajax.complete($form , formData , settings.texts[message] || message , ret);
								if	(ret===false)			return;
								else if (ret!==null || ret!==undefined) message=ret;
							}

							if (typeof(message)=='string') {
								message = settings.texts[message] || message;
								if (settings.tooltip && $submitButton) {
									$submitButton.tooltip('show' , message , settings.tooltip);
									clearTimeout(submitTooltipTmr);
									submitTooltipTmr = setTimeout(function() {$submitButton.tooltip(); } , 2000);
								}
								else alert(message);
							}
						})
						.error(function(jqXHR, textStatus, errorThrown) {
							alert(settings.texts['connectionError'] || 'ERROR: connectionError');
						})
					;
					return false;
				}
				// if no ajax, return true to send form
				else return true;
			}
		});

		function checkField(elm) {
			var error;
			var $elm	= $(elm);
			var value	= $elm.val();

			// if value only exists out of whitespace characters
			if (value.match(/^\s+$/)) $elm.val(value='');

			// if value is required, but not defined
			if	(($elm.hasClass('req') || $elm.hasClass('required')) && (!value || value==elm.title)) {
				error = {$elm:$elm , id:'requiredField'};
			}
			// if value is supposed to be an e-mailaddress
			else if	(($elm.hasClass('mail') || $elm.hasClass('email')) && value && !value.match(/^[a-z0-9\-._]+@[a-z0-9\-.]{2,}\.[a-z0-9]{2,6}$/i)) {
				error = {$elm:$elm , id:'invalidEmailaddress'};
			}
			else if (settings.funcs.checkField) {
				var ret = settings.funcs.checkField($elm , elm , elm.name , elm.value);

				// normally a check would return FALSE on error, but we expect an error string which is a TRUE return
				// so to prevent confusion, do not allow a boolean return
				if	(ret===true || ret===false)	alert('ERROR: custom checkField function should return an error message/id instead of a boolean');
				else if	(ret)				error = {$elm:$elm , id:ret};
			}

			// if error object is defined
			if (error) {
				// set error message
				error.message = settings.texts[error.id] || error.id;

				// show tooltip
				if (settings.tooltip) $elm.tooltip('show' , error.message , settings.tooltip);

				// add class and bind events to check on value change
				if (!$elm.hasClass('error')) {
					$elm.addClass('error');

					// if textfield or textarea
					if ($elm.is('input[type=text]') || $elm.is('textarea')) {
						$elm.bind({
							'keyup.handleForms':	function(e) {checkField(this); },
							'paste.handleForms':	function(e) {checkField(this); }
						});
					}
					else if (1) {
// select, checkbox, radiobutton
					}
				}
				return error;
			}
			// remove toolip
			else {
				if ($elm.hasClass('error')) {
					if (settings.tooltip) $elm.tooltip();
					$elm.removeClass('error');
//					$elm.unbind('.handleForms');
				}
			}
		}

		function makeHtmlValue(elm) {
			var $elm = $(elm);
			var $htmlField = $form.find("input[name='_html_" + elm.name + "']");
			if ($htmlField.length>0) {
				var html	= settings.funcs.makeHtmlValue ? settings.funcs.makeHtmlValue($elm , elm , elm.name , elm.value):null;

				// make standard moderations if html is null/undefined
				if (html===null || html===undefined) {
					html	= $elm.val();

					// make anchortag of e-mailaddresses
					if (elm.name.match(/e-?mail/i) && html.match(/^[\w-.]+@[\w-.]+$/)) html="<a href='mailto:" + html + "'>" + html + '</a>';

					// turn newlines into break tags
					html	= html.replace(/\n/g , '<br />\n');
				}

				$htmlField.val(html);
			}
		}
	});
}

