/* ----------------------------------------------------------------
 * Original File Name:  Validator.js
 * Creation Date:       01.09.2005
 * Description:         Class responsible for client side input validation
 * -----------------------------------------------------------------
 
 * -----------------------------------------------------------------
 * Copyright (c) 2005 BestSolution.at EDV Systemhaus GmbH
 * All Rights Reserved.
 *
 * BestSolution.at MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT
 * THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT.
 * BestSolution.at SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED
 * BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS
 * SOFTWARE OR ITS DERIVATIVES.
 *
 * This software is released under the terms of the 
 *
 *               "GNU General Public License, Version 2" 
 *
 * and may only be distributed and used under the terms of the mentioned 
 * license. You should have received a copy of the license along
 * this software product, if not you can download it from
 * http://www.gnu.org/licenses/gpl.html
 * ----------------------------------------------------------------

 * ----------------------------------------------------------------
 * $HeadURL: https://morpheus.bestsolution.at/repos/gejenbacher/gw03/trunk/src/scripts/Validator.js $
 * $Revision: 14873 $
 * Creator: Udo
 * Creation Date: 25.10.2005
 *
 * $LastChangedDate: 2009-03-06 14:34:24 +0100 (Fri, 06 Mar 2009) $
 * $LastChangedBy: thomas $
 * ----------------------------------------------------------------
 */

/**
 * Class responsible for client side input validation
 */
function Validator()
{
	this.checkForm = null;
	this.fieldsMandatory = new Array();
	this.fieldsMandatoryFailMessage = new Array();
	this.checksCustom = new Array();
	this.checksCustomFailMessage = new Array();
	this.checksCustomFocusField = new Array();
	this.fieldsPatternized = new Array();
	this.fieldsPatternizedFailMessage = new Array();
	this.fieldsPatternizedPattern = new Array();
	this.fieldsEqual = new Array();
	this.fieldsEqualCompare = new Array();
	this.fieldsEqualFailMessage = new Array();
	this.fieldsNumeric = new Array();
	this.fieldsNumericMin = new Array();
	this.fieldsNumericMax = new Array();
	this.fieldsNumericFailMessage = new Array();
	this.fieldsDecimal = new Array();
	this.fieldsDecimalFailMessage = new Array();
	this.fieldsDependent = new Array();
	this.fieldsDependentFailMessage = new Array();
	this.fieldsDependentOrigin = new Array();
	this.fieldsDependentOriginValue = new Array();
	this.fieldsDateDay = new Array();
	this.fieldsDateMonth = new Array();
	this.fieldsDateYear = new Array();
	this.fieldsDateFailMessage = new Array();
	this.tagsNoSpecial = new Array();
	this.tagsNoSpecialFailMessage = new Array();
	this.fieldsEmail = new Array();
	this.fieldsEmailFailMessage = new Array();
	
	this.fieldsDateDaySmaller = new Array();
	this.fieldsDateMonthSmaller = new Array();
	this.fieldsDateYearSmaller = new Array();
	this.fieldsDateDayGreater = new Array();
	this.fieldsDateMonthGreater = new Array();
	this.fieldsDateYearGreater = new Array();
	this.fieldsDatePlausibilityFailMessage = new Array();
	
	this.fieldsDateDiffFDay = new Array();
	this.fieldsDateDiffFMonth = new Array();
	this.fieldsDateDiffFYear = new Array();
	this.fieldsDateDiffSDay = new Array();
	this.fieldsDateDiffSMonth = new Array();
	this.fieldsDateDiffSYear = new Array();
	this.fieldsDateDiffSpan = new Array();
	this.fieldsDateDiffFailMessage = new Array();
	
	
	this.registerForm = registerForm;
	this.addMandatoryField = addMandatoryField;
	this.addCustomCheck = addCustomCheck;
	this.addPatternField = addPatternField;
	this.addEqualField = addEqualField;
	this.addNumericField = addNumericField;
	this.addDecimalField = addDecimalField;
	this.addDependentMandatoryField = addDependentMandatoryField;
	this.addDateField = addDateField;
	this.addEmailField = addEmailField;
	this.addNoSpecialCharacterTag = addNoSpecialCharacterTag;
	this.addDateGreaterThanDate = addDateGreaterThanDate;
	this.addDateDiffCheck = addDateDiffCheck;
	this.doValidation = doValidation;
	this.isEmpty = isEmpty;
	this.getFieldObject = getFieldObject;
	this.checkUpdateable = checkUpdateable;
	this.removeField = removeField;
	
	this.invalidObj = null;
	this.oldStyleOfInvalidObj = null;

	/**
	 * registers the from to use for checking the fields
	 * @param formObj the form DOM object
	 */
	function registerForm( formObj )
	{
		this.checkForm = formObj;
	}
	
	/**
	 * adds a field that will be forced to be non empty
	 * @param fieldName the name of the field to add (not the object!)
	 * @param failMessage the message to display, if the field is left empty
	 */
	function addMandatoryField( fieldName, failMessage )
	{
		this.fieldsMandatory.push( fieldName );
		this.fieldsMandatoryFailMessage.push( failMessage );
		return '1-'+(this.fieldsMandatory.length-1);
	}
	
	/**
	 * adds a field that will be checked against the given pattern
	 * @param fieldName the name of the field to add (not the object!)
	 * @param failMessage the message to display, if the field is left empty
	 * @param checkPattern the pattern used for validation
	 */
	function addPatternField( fieldName, failMessage, checkPattern )
	{
		this.fieldsPatternized.push( fieldName );
		this.fieldsPatternizedFailMessage.push( failMessage );
		this.fieldsPatternizedPattern.push( checkPattern );
		return '2-'+(this.fieldsPatternized.length-1);
	}

	/**
	 * adds a third party validation method
	 * @param checkName the <b>name</b> of the third party method to call (no reference/anonymous definition/...)
	 * @param failMessage the message to display, if the validation fails
	 * @param focusField optionally the <b>fieldname</b> of the field to give focus to if the check fails
	 */
	function addCustomCheck( checkName, failMessage, focusField )
	{
		this.checksCustom.push( checkName );
		this.checksCustomFailMessage.push( failMessage );

		if ( focusField )
			this.checksCustomFocusField.push( focusField );
		else
			this.checksCustomFocusField.push( null );
	}
	
	/**
	 * compares the two fields and displays the given error 
	 * message, if the are not equal
	 * @param fieldName the name of the field to add
	 * @param failMessage the message to display, if the validation fails
	 * @param equalFieldName the second field to compare with
	 */
	function addEqualField( fieldName, failMessage, equalFieldName )
	{
		this.fieldsEqual.push( fieldName );
		this.fieldsEqualCompare.push( equalFieldName );
		this.fieldsEqualFailMessage.push( failMessage );
	}

	/**
	 * Adds a field that will be forced to contain only numeric values
	 * of the given range. Please note that the forcing does not include
	 * checking if there is a value entered at all (mandatory fields), that
	 * can be done with the addMandatoryField method.
	 * @param fieldName the name of the field to add (not the object!)
	 * @param limitMin the lower numeric limit
	 * @param limitMax the upper numeric limit
	 * @param failMessage the message to display, if the field is left empty
	 */
	function addNumericField( fieldName, limitMin, limitMax, failMessage )
	{
		this.fieldsNumeric.push( fieldName );
		this.fieldsNumericMin.push( limitMin );
		this.fieldsNumericMax.push( limitMax );
		this.fieldsNumericFailMessage.push( failMessage );
		return '5-'+( this.fieldsNumeric.length -1 );
	}
	
	/**
	 * Adds a field that will be forced to contain only decimal values
	 * of the given range. Please note that the forcing does not include
	 * checking if there is a value entered at all (mandatory fields), that
	 * can be done with the addMandatoryField method.
	 * @param fieldName the name of the field to add (not the object!)
	 * @param failMessage the message to display, if the field is left empty
	 */
	function addDecimalField( fieldName, failMessage ) {
		this.fieldsDecimal.push ( fieldName );
		this.fieldsDecimalFailMessage.push ( failMessage );
	}
	
	/**
	 * Adds a field that will be forced to be non empty, but only
	 * if a second, independent field has the given value.
	 * @param fieldName the name of the field to add (not the object!)
	 * @param failMessage the message to display, if the field is left empty
	 * @param originFieldName the name of the second field to check
	 * @param originPrerequisiteValue the value that the origin field has to have 
	 *			in order to trigger checking of the other dependent field
	 */
	function addDependentMandatoryField( fieldName, failMessage, originFieldName, originPrerequisiteValue )
	{
		this.fieldsDependent.push( fieldName );
		this.fieldsDependentFailMessage.push( failMessage );
		this.fieldsDependentOrigin.push( originFieldName );
		this.fieldsDependentOriginValue.push( originPrerequisiteValue );
	}
	
	/**
	 * Adds 3 fields that have to be a valid date.
	 * @param dayField the name of the day field
	 * @param monthField the name of the month field
	 * @param yearField the name of the year field
	 * @param failMessage the message to display, if it is no valid date
	 */	 
	function addDateField( dayField, monthField, yearField, failMessage ) {
		this.fieldsDateDay.push ( dayField );
		this.fieldsDateMonth.push ( monthField );
		this.fieldsDateYear.push ( yearField );
		this.fieldsDateFailMessage.push ( failMessage );
	}
	/**
	 * Adds a field that will be forced to contain a valid e-mail address.
	 * Please note that the forcing does not include
	 * checking if there is a value entered at all (mandatory fields), that
	 * can be done with the addMandatoryField method.
	*/
	function addEmailField( fieldName, failMessage ){
		this.fieldsEmail.push( fieldName );
		this.fieldsEmailFailMessage.push( failMessage );
	}
	
	
	/**
	 * Adds a tag (like textarea); a value from a field of such a type
	 * must not contain a special character
	 * @param tag the name of the tag
	 * @param failMessage message to display, if a such a field contains a special char
	 */
	function addNoSpecialCharacterTag( tag, failMessage ) {
		this.tagsNoSpecial.push( tag );
		this.tagsNoSpecialFailMessage.push( failMessage );	
	}
	
	/**
	* Adds two date fields.
	* The first one has to be greater than the second one.
	* @param gDayField the name of the day field from the first date
	* @param gMonthField the name of the month field from the first date
	* @param gYearField the name of the year field from the first date
	* @param sDayField the name of the day field from the second date
	* @param sMonthField the name of the month field from the second date
	* @param sYearField the name of the year field from the second date
	* @param failMessage message to display, if the first one isn't greater than the second one
	*/
	function addDateGreaterThanDate( gDayField, gMonthField, gYearField, sDayField, sMonthField, sYearField, failMessage ) {
		this.fieldsDateDayGreater.push( gDayField );
		this.fieldsDateMonthGreater.push( gMonthField );
		this.fieldsDateYearGreater.push( gYearField );
		this.fieldsDateDaySmaller.push( sDayField );
		this.fieldsDateMonthSmaller.push( sMonthField );
		this.fieldsDateYearSmaller.push( sYearField );
		this.fieldsDatePlausibilityFailMessage.push( failMessage );
	}
	
	/**
	* Adds two date fields and the allowed span between them.
	* @param fDayField the name of the day field form the first date
	* @param fMonthField the name of the month field form the first date
	* @param fYearField the name of the year field form the first date
	* @param sDayField the name of the day field form the second date
	* @param sMonthField the name of the month field form the second date
	* @param sYearField the name of the year field form the second date
	* @param allowedDiffDays the allowed span between the two dates (can either be positiv or negativ)
	* @param failMessage message to display, if the dates are to far off
	*/
	function addDateDiffCheck( fDayField, fMonthField, fYearField, sDayField, sMonthField, sYearField, allowedDiffDays, failMessage ) {
		this.fieldsDateDiffFDay.push( fDayField );
		this.fieldsDateDiffFMonth.push( fMonthField );
		this.fieldsDateDiffFYear.push( fYearField );
		this.fieldsDateDiffSDay.push( sDayField );
		this.fieldsDateDiffSMonth.push( sMonthField );
		this.fieldsDateDiffSYear.push( sYearField );
		this.fieldsDateDiffSpan.push( allowedDiffDays );
		this.fieldsDateDiffFailMessage.push( failMessage );
	}
	
	
	function removeField( id ) {
		if ( id ) {
			var whichArray = id.substring( 0, id.indexOf( '-', 0 ) );
			var index = id.substring( id.indexOf( '-' )+1 , id.length );
			switch( whichArray ) {
				case '1':
					var helpArray = this.fieldsMandatory.slice( parseInt( index ) + 1);
					this.fieldsMandatory = this.fieldsMandatory.slice( 0, parseInt( index ) );
					this.fieldsMandatory.concat( helpArray ); 
					helpArray = this.fieldsMandatoryFailMessage.slice( parseInt( index ) + 1);
					this.fieldsMandatoryFailMessage = this.fieldsMandatoryFailMessage.slice( 0, parseInt( index ) );
					this.fieldsMandatoryFailMessage.concat( helpArray );
					break;
				case '5':
					var helpArray = this.fieldsNumeric.slice( parseInt( index ) + 1);
					this.fieldsNumeric = this.fieldsNumeric.slice( 0, parseInt( index ) );
					this.fieldsNumeric.concat( helpArray ); 
					helpArray = this.fieldsNumericMin.slice( parseInt( index ) + 1);
					this.fieldsNumericMin = this.fieldsNumericMin.slice( 0, parseInt( index ) );
					this.fieldsNumericMin.concat( helpArray );
					helpArray = this.fieldsNumericMax.slice( parseInt( index ) + 1);
					this.fieldsNumericMax = this.fieldsNumericMax.slice( 0, parseInt( index ) );
					this.fieldsNumericMax.concat( helpArray );
					helpArray = this.fieldsNumericFailMessage.slice( parseInt( index ) + 1);
					this.fieldsNumericFailMessage = this.fieldsNumericFailMessage.slice( 0, parseInt( index ) );
					this.fieldsNumericFailMessage.concat( helpArray );
					break;
				default:
					break;
			}
		}
	}
	
	/**
	 * central entry point for the validation process
	 * @return true or false, depending on the outcome of the validation
	 */
	function doValidation()
	{
		var rv = true;
		
		if ( this.invalidObj != null ) {
			this.invalidObj.className = this.oldStyleOfInvalidObj;
		}
			
		// first check all mandatory fields
		rv = validateMandatoryFields( this );

		// only continue, if the previous checks were ok
		if ( rv ) {
			// dependent checks
			rv = validateDependentMandatoryChecks( this );
		}
		if ( rv ) {
			// perform pattern checks
			rv = validatePatternChecks( this );
		}
		if ( rv ) {
			// equality checks
			rv = validateEqualityChecks( this );
		}
		if ( rv ) {
			// numeric checks
			rv = validateNumericChecks( this );
		}
		if ( rv ) {
			// decimal checks
			rv = validateDecimalChecks( this );
		}
		if ( rv ) { 
			// date checks
			rv = validateDateChecks( this );
		}
		if ( rv ) {
			//email checks
			rv = validateEmailChecks( this );
		}
		if ( rv ) {
			// then look for custom checkers
			rv = validateCustomChecks( this );
		}
		if ( rv ) {
			rv = validateNoSpecialCharacterChecks( this );
		}
		if ( rv ) {
			rv = validateDatePlausibilityChecks( this );
		}
		
		if ( rv ) {
			rv = validateDateDiffChecks( this );
		}
		return rv;
	}
	
	/**
	 * validates the registered mandatory fields
	 * @return true or false, depending on the outcome of the validation
	 */
	function validateMandatoryFields( self )
	{
		var rv = true;
		for ( var oneField in self.fieldsMandatory ) { 
			if (typeof(self.fieldsMandatory[oneField]) == 'string') {
				if (self.checkUpdateable(self.getFieldObject(self.fieldsMandatory[oneField]))) {
					if (self.isEmpty(self.fieldsMandatory[oneField])) {
						displayMessage(self.fieldsMandatoryFailMessage[oneField], self.getFieldObject(self.fieldsMandatory[oneField]), self);
						rv = false;
						break;
					}
				}
			}
		}	
		return rv;
	}
	
	/**
	 * validates the registered patterns
	 * @return true or false, depending on the outcome of the validation
	 */
	function validatePatternChecks( self )
	{
		var rv = true;
		var fieldObj;
		
		for ( var oneField in self.fieldsPatternized )
		{
			if (typeof(self.fieldsPatternized[oneField]) == 'string') {
				fieldObj = self.getFieldObject(self.fieldsPatternized[oneField]);
				if (self.checkUpdateable(fieldObj)) {
					if (!fieldObj.value.match(self.fieldsPatternizedPattern[oneField])) {
						displayMessage(self.fieldsPatternizedFailMessage[oneField], fieldObj, self);
						rv = false;
						break;
					}
				}
			}
		}

		return rv;
	}

	/**
	 * validates the registered equal fields against
	 * the registered comparison fields
	 * @return true or false, depending on the outcome of the validation
	 */
	function validateEqualityChecks( self )
	{
		var rv = true;
		var fieldObj;
		
		for ( var oneField in self.fieldsEqual )
		{
			if (typeof(self.fieldsEqual[oneField]) == 'string') {
				fieldObj = self.getFieldObject(self.fieldsEqual[oneField]);
				if (checkUpdateable(fieldObj)) {
					if (fieldObj.value != (self.getFieldObject(self.fieldsEqualCompare[oneField])).value) {
						displayMessage(self.fieldsEqualFailMessage[oneField], fieldObj, self);
						rv = false;
						break;
					}
				}
			}
		}
		return rv;
	}

	/**
	 * validates the registered numeric fields for their
	 * lower and upper limits
	 * @return true or false, depending on the outcome of the validation
	 */
	function validateNumericChecks( self )
	{
		var rv = true;
		var fieldObj;
		
		for ( var oneField in self.fieldsNumeric )
		{
			if (typeof(self.fieldsNumeric[oneField]) == 'string') {
				fieldObj = self.getFieldObject(self.fieldsNumeric[oneField]);
				// we allow emtpy values!
				if (checkUpdateable(fieldObj)) {
					if (!self.isEmpty(fieldObj.name) &&
					(!fieldObj.value.match(/^\d+$/) ||
					fieldObj.value < self.fieldsNumericMin[oneField] ||
					fieldObj.value > self.fieldsNumericMax[oneField])) {
						displayMessage(self.fieldsNumericFailMessage[oneField], fieldObj, self);
						rv = false;
						break;
					}
				}
			}
		}
		return rv;
	}
	
	function validateDecimalChecks( self ) {
		var rv = true;
		var fieldObj;
		
		for ( var oneField in self.fieldsDecimal ) {
			if (typeof(self.fieldsDecimal[oneField]) == 'string') {
				fieldObj = self.getFieldObject(self.fieldsDecimal[oneField]);
				if (checkUpdateable(fieldObj)) {
					if (!self.isEmpty(fieldObj.name) && (!fieldObj.value.match(/^((\d+(\,\d*)?)|((\d*\,)?\d+))$/))) {
						displayMessage(self.fieldsDecimalFailMessage[oneField], fieldObj, self);
						rv = false;
						break;
					}
				}
			}
		}
		return rv;
	}

	/**
	 * validates the registered dependent mandatory fields
	 * @return true or false, depending on the outcome of the validation
	 */
	function validateDependentMandatoryChecks( self )
	{
		var rv = true;
		
		for ( var oneField in self.fieldsDependent )
		{	
		if (typeof(self.fieldsDependent[oneField]) == 'string') {
			if (checkUpdateable(self.getFieldObject(self.fieldsDependent[oneField]))) {
				if (extractValue(self, self.fieldsDependentOrigin[oneField]) == self.fieldsDependentOriginValue[oneField] &&
				self.isEmpty(self.fieldsDependent[oneField])) {
					displayMessage(self.fieldsDependentFailMessage[oneField], self.getFieldObject(self.fieldsDependent[oneField]), self);
					rv = false;
					break;
				}
			}
		}
		}
			
		return rv;
	}
	
	/**
	 * validates the registered date fields
	 * @return true if all dates are valid, elsewise false
	 */
	function validateDateChecks( self ) {
		var rv = true;
		var date = '';
	    // matches all kinds of "german" date types
		var RegExPattern = /^((((0?[1-9]|[12]\d|3[01])[\.\-\/](0?[13578]|1[02])[\.\-\/]((1[6-9]|[2-9]\d)\d{2}))|((0?[1-9]|[12]\d|30)[\.\-\/](0?[13456789]|1[012])[\.\-\/]((1[6-9]|[2-9]\d)\d{2}))|((0?[1-9]|1\d|2[0-8])[\.\-\/]0?2[\.\-\/]((1[6-9]|[2-9]\d)\d{2}))|(29[\.\-\/]0?2[\.\-\/]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00)))|(((0[1-9]|[12]\d|3[01])(0[13578]|1[02])((1[6-9]|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)(0[13456789]|1[012])((1[6-9]|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])02((1[6-9]|[2-9]\d)\d{2}))|(2902((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00))))$/;
		
		for ( var oneField in self.fieldsDateDay ) {
			if (typeof(self.fieldsDateDay[oneField]) == 'string') {
				date = extractValue(self, self.fieldsDateDay[oneField]) + '/' + extractValue(self, self.fieldsDateMonth[oneField]) + '/' + extractValue(self, self.fieldsDateYear[oneField]);
				if (date != '//') {
					if (!date.match(RegExPattern)) {
						displayMessage(self.fieldsDateFailMessage[oneField], self.getFieldObject(self.fieldsDateDay[oneField]), self);
						rv = false;
						break;
					}
				}
				date = '';
			}
		}
		return rv;
	
	}
	
	/**
	 * validates the registered e-mail fields.
	 * the used regular expression is a slight modification of the RFC 2822 standard
	 * and can be found at http://www.regular-expressions.info/email.html
	 */
	function validateEmailChecks( self ) {
		var rv = true;
		var email = '';
		
		var regExpression = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
	
		for ( var oneField in self.fieldsEmail ) {
				if (typeof(self.fieldsEmail[oneField]) == 'string') {
					if (checkUpdateable(self.getFieldObject(self.fieldsEmail[oneField]))) {
						email = extractValue(self, self.fieldsEmail[oneField]);
						if (email != '') {
							if (!email.match(regExpression)) {
								displayMessage(self.fieldsEmailFailMessage[oneField], self.getFieldObject(self.fieldsEmail[oneField]), self);
								rv = false;
								break;
							}
						}
					}
					email = '';
				}
		}
		return rv;
	}
	
	function extractValue(self, fieldname) {
		var field = self.getFieldObject( fieldname );
		
		if( ! field.length ) {
			if ( field.type == "radio" || field.type == "checkbox" ) {
				if( field.checked ) {
					return field.value;
				}
			}
			else {						
				return field.value;
			}
		} else {
			for(var i = 0; i < field.length; i++ ) {
				if( field[i].checked ) {
					return field[i].value;
				}
			}
		}
		
		return null;
	}
	
	/**
	 * validates the registered custom checks
	 * @return true or false, depending on the outcome of the validation
	 */
	function validateCustomChecks( self )
	{
		var rv = true;
		
		for ( var oneCheck in self.checksCustom ) {
			if ( ! eval(self.checksCustom[oneCheck]) )
			{
				displayMessage( self.checksCustomFailMessage[oneCheck], self.getFieldObject( self.checksCustomFocusField[oneCheck] ), self );
				rv = false;
				break;
			}
		}
		return rv;
	}
	
	/** 
	 * validates the registerd special char checks
	 */
	function validateNoSpecialCharacterChecks( self ) {
		var rv = true;
		var elements;
			for ( var tagname in self.tagsNoSpecial ) {
				if (typeof(self.tagsNoSpecial[tagname]) == 'string') {
					elements = document.getElementsByTagName(self.tagsNoSpecial[tagname]);
					for (var counter in elements) {
						if (checkUpdateable(elements[counter])) {
							if (elements[counter].value && (elements[counter].value.indexOf("'") != -1 || elements[counter].value.indexOf('"') != -1)) {
								displayMessage(self.tagsNoSpecialFailMessage[tagname], elements[counter], self);
								rv = false;
								break;
							}
						}
					}
					if (rv == false) {
						break;
					}
				}
			}
		return rv;
	}
	
	/**
	* validates the registered date plausibility checks
	*/
	function validateDatePlausibilityChecks ( self ) {
		var rv = true;
		var greaterDate;
		var smallerDate;
			for ( var day in self.fieldsDateDayGreater ) {
				if (typeof(self.fieldsDateDayGreater[day]) == 'string') {
					greaterDate = new Date(self.getFieldObject(self.fieldsDateYearGreater[day]).value, self.getFieldObject(self.fieldsDateMonthGreater[day]).value - parseInt(1), self.getFieldObject(self.fieldsDateDayGreater[day]).value);
					smallerDate = new Date(self.getFieldObject(self.fieldsDateYearSmaller[day]).value, self.getFieldObject(self.fieldsDateMonthSmaller[day]).value - parseInt(1), self.getFieldObject(self.fieldsDateDaySmaller[day]).value);
					if (greaterDate < smallerDate && greaterDate > 0 && smallerDate > 0) {
						displayMessage(self.fieldsDatePlausibilityFailMessage[day]);
						rv = false;
						break;
					}
				}
			}
		return rv;
	}
	
	/**
	* validates the registered date diff plausibility checks
	* does only return false if a check fails AND if the the showed dialog is answerd with NO
	*/
	function validateDateDiffChecks ( self ) {
		var rv = true;
		var firstDate;
		var secondDate;
			for( var day in self.fieldsDateDiffFDay ) {
				if (typeof(self.fieldsDateDiffFDay[day]) == 'string') {
					firstDate = new Date(self.getFieldObject(self.fieldsDateDiffFYear[day]).value, self.getFieldObject(self.fieldsDateDiffFMonth[day]).value - parseInt(1), self.getFieldObject(self.fieldsDateDiffFDay[day]).value);
					secondDate = new Date(self.getFieldObject(self.fieldsDateDiffSYear[day]).value, self.getFieldObject(self.fieldsDateDiffSMonth[day]).value - parseInt(1), self.getFieldObject(self.fieldsDateDiffSDay[day]).value);
					;
					if (firstDate > 0 && secondDate > 0) {
						secondDate.setDate(secondDate.getDate() - parseInt(self.fieldsDateDiffSpan[day]));
						if (firstDate <= secondDate) {
							if (!confirm(self.fieldsDateDiffFailMessage[day])) {
								rv = false;
								break;
							}
						}
					}
				}
			}
		return rv;
	}
	
	/**
	 * returns the given field as an queryable object
	 * @param fieldName the name of the field to query
	 * @return a DOM field object
	 */
	function getFieldObject( fieldName )
	{
		return ( fieldName ) ? this.checkForm.elements[fieldName] : null;
	}

	/**
	 * checks if the given field is empty, and if so,
	 * return true, see also checkEmpty
	 * @param fieldName the name of the field to query
	 * @return true, if the field is empty, false if not
	 */
	function isEmpty( fieldName )
	{
		var oneField = this.checkForm.elements[fieldName];
		var rv = true;
		var fieldListObj;

		// well, one dimensional objects have a name,
		// whereas arrays (such as checkbox lists) don't :-)
		if ( (typeof(oneField['value']) != "undefined") || (typeof(oneField['checked']) != "undefined") ) {
			rv = checkEmpty( this, oneField );
		}
		else 
		{ 
			for ( var fieldCounter = 0; fieldCounter < oneField.length; fieldCounter++ )
			{
				fieldListObj = oneField[fieldCounter];
				if ( ! checkEmpty( this, fieldListObj ) )
				{
					rv = false;
					break;
				}
			}
		}
		
		return rv;
	}
	
	/**
	 * checks if the given field object is empty, and if so,
	 * return true. The difference between isEmpty and checkEmpty
	 * is that checkEmpty works on the field object level and
	 * thus is able to correctly differentiate between the various 
	 * field types
	 * @param fieldObj the field object to investigate
	 * @return true, if the field is empty, false if not
	 */
	function checkEmpty( self, fieldObj )
	{
		var rv = false;

		switch ( fieldObj.type )
		{
			case "file":
			case "text":
			case "password":
			case "textarea":
			case "hidden":
			case "select-one":
				rv =  ( fieldObj.value == null || fieldObj.value.replace(/^\s*|\s*$/g,"") == "" ) ? true : false;
				break;
			case "checkbox":
			case "radio":
				rv = ! fieldObj.checked;
				break;
		}
		
		return rv;
	}
	
	/**
	 * displays the fail message and gives focus to the 
	 * erratic field (if it is a textfield)
	 * @param fieldObj the field object to investigate
	 * @return true, if the field is empty, false if not
	 */
	function displayMessage( message, fieldObj, self )
	{
		alert(message);
		// set focus for text fields
		if ( fieldObj && fieldObj.name ) {
			fieldObj.focus();
			self.invalidObj = fieldObj;
			self.oldStyleOfInvalidObj = fieldObj.className;
			fieldObj.className = 'error';
		}
	}
	
	/**
	 *
	 * @param fieldObj the field object to investigate
	 * @return true, if the field is updateable
	 */
	function checkUpdateable( fieldObj ) {
		var rv = true;

		switch ( fieldObj.type )
		{
			case "file":
			case "text":
			case "password":
			case "textarea":
			case "select-one":
				rv =  ( fieldObj.disabled == true || fieldObj.readOnly == true ) ? false : true;
				break;
			case "hidden": 
				rv = false;
				break;
		}
		
		return rv;
	}

	
}
