/*=======================================================
 * This is a set of functions that:
 *    beautify fields with a certain format and also can
 *    set or remove the corresponding error messages
 * unfortunatelly, this has to be in an ISML cause the .js files
 * are not beeing procxessed by the server sided software.
 =======================================================*/
/**
 * this is an abstract class defining all attributes expected by the
 * validation framework
 */
function Beautifier( fieldName )
{
	Beautifier.prototype.errorTexts;
	
	if ( typeof Beautifier.prototype.__bf_initialized == "undefined" )
	{
		Beautifier.prototype.ERROR_NO_ERROR =  0;
		// Beautifier.prototype.errorTexts     = new Array();
		// Beautifier.prototype.errorTexts[this.ERROR_NO_ERROR] = "";

		Beautifier.prototype.lastError = this.ERROR_NO_ERROR;

		Beautifier.prototype.fieldName = fieldName;
		
		/**
		 *  This function is required by the initiating framework.
		 *  Better might be a function "getField()"  or may be  
		 *  returning fieldName plus formName...
		 */
		Beautifier.prototype.getFieldName = function()
		{
			return this.args.fieldName;
		}
		
		// this method needs to be overwritten!
		// return new Array( beautitied Text, error number, args to format error msg );
		// example for Dates:
		// return new Array( "1/1/2008", DATE_FORMAT_DATE_TOO_BIG, new Array( "1/1/1980" ) );
		// This will result in an errormessage like:  "The date needs to be before 1/1/1980"
		Beautifier.prototype.formatText = function( rawData )
		{
			alert( "this function is not implemented yet!!!" );
		}
		
		/*
		 * The implementation of this is optional.  I intend to 
		 * call this method from the <form onsubmit>
		 */
		Beautifier.prototype.isSubmitable = function()
		{
			return this.lastError == this.ERROR_NO_ERROR;
		}
		
		Beautifier.prototype.__bf_initialized = true;
	}
}
/**
 * Ths function/class hold all required methods and attributes
 * for ensuring the proper formatting of a date field.
 * Some additional functions like trim() or parseInt() are myParseInt are required!
 *  
 * @param args      : structure like the following
 * 			fieldName : mandatory: String : name of the field we are dealing with here
 *          formatStr : mandatory: String : String representing the date format.  Currently only half fearted US supported
 *          required  : optional : Boolean: makes the field required only if set to TRUE
 *          min       : optional : Date   : earliest valid date; dates before than this will be marked errornous 
 *          max       : optional : Date   : latest valid date; dates past this will be marked errornous 
 */
function DateFormatter( args )
{
	Beautifier.call( this, args.fieldName );
	this.args = args;
	
	this.formatStr = args.formatStr;
	if ( typeof DateFormatter.prototype.__df_initialized == "undefined" )
	{
		DateFormatter.prototype.DATE_FORMAT_INVALID_FORMATTER =  1;
		DateFormatter.prototype.DATE_FORMAT_EXTRA_CHAR        =  2;
		DateFormatter.prototype.DATE_FORMAT_TOO_SHORT         =  3;
		DateFormatter.prototype.DATE_FORMAT_INVALID_DAY       =  4;
		DateFormatter.prototype.DATE_FORMAT_INVALID_MONTH     =  5;
		DateFormatter.prototype.DATE_FORMAT_INVALID_YEAR      =  6;
		DateFormatter.prototype.DATE_FORMAT_DATE_IN_PAST      =  7;
		DateFormatter.prototype.DATE_FORMAT_DATE_IN_FUTURE    =  8;
		DateFormatter.prototype.DATE_FORMAT_DATE_TOO_BIG      =  9;
		DateFormatter.prototype.DATE_FORMAT_DATE_TOO_SMALL    = 10;
		DateFormatter.prototype.DATE_FORMAT_MISSING_VALUE     = 11;
		
		//  This is the array with error texts that is required buy the initiating
		//  framework.  The numbers must match the number returned by the formatText 
		//  function.
		DateFormatter.prototype.errorTexts     = new Array();
		this.errorTexts[this.DATE_FORMAT_INVALID_FORMATTER] = BeautifierErrorText["forms.profile.parseDateInvalidFormatString"];
		this.errorTexts[this.DATE_FORMAT_EXTRA_CHAR     ] = BeautifierErrorText["forms.profile.parseDateUseNumbersOnly"];
		this.errorTexts[this.DATE_FORMAT_TOO_SHORT      ] = BeautifierErrorText["forms.profile.parseDateTooShort"];
		this.errorTexts[this.DATE_FORMAT_INVALID_DAY    ] = BeautifierErrorText["forms.profile.parseDateInvalidDay"];
		this.errorTexts[this.DATE_FORMAT_INVALID_MONTH  ] = BeautifierErrorText["forms.profile.parseDateInvalidMonth"];
		this.errorTexts[this.DATE_FORMAT_INVALID_YEAR   ] = BeautifierErrorText["forms.profile.parseDateInvalidYear"];
		this.errorTexts[this.DATE_FORMAT_DATE_IN_PAST   ] = BeautifierErrorText["forms.profile.parseDateInPast"];
		this.errorTexts[this.DATE_FORMAT_DATE_IN_FUTURE ] = BeautifierErrorText["forms.profile.parseDateInFuture"];
		this.errorTexts[this.DATE_FORMAT_DATE_TOO_BIG   ] = BeautifierErrorText["forms.profile.parseDatePast"];
		this.errorTexts[this.DATE_FORMAT_DATE_TOO_SMALL ] = BeautifierErrorText["forms.profile.parseDateBefore"];
		this.errorTexts[this.DATE_FORMAT_MISSING_VALUE  ] = BeautifierErrorText["forms.profile.missingValue"];
		
		/**
		 * internal function, formats a date object basically to american format
		 */
		DateFormatter.prototype.__formatIt = function( dateObj, fmtArr )
		{
			var ret = "";
			for ( i = 0; i < fmtArr.length; i++ )
			{
				// there might be someone else making a difference between
				// uppdercase/ lowercase, but I'll ignore it here.
				switch ( fmtArr[i].charAt( 0 ) )
				{
					case 'D' :
					case 'd' :
						if ( fmtArr[i].length == 2 && dateObj.getDate() < 10 )
						{
							ret += "0" + dateObj.getDate();
						}
						else
						{
							ret += dateObj.getDate();
						}
						break;
					case 'M' :
					case 'm' :
						if ( fmtArr[i].length == 2 && dateObj.getMonth() < 9 )
						{
							ret += "0" + (1 + dateObj.getMonth());
						}
						else
						{
							ret += (1 + dateObj.getMonth());
						}
						break;
					case 'Y' :
					case 'y' :
						if ( fmtArr[i].length == 2 )
						{
							ret += new String( dateObj.getFullYear() ).substring( 2 );
						}
						else
						{
							ret += dateObj.getFullYear();
						}
				}
				ret += '/';
			}
			return ret.substring( 0, ret.length - 1 );
		}
		
		/**
		 * the missing value is captured from the backend
		 */
		DateFormatter.prototype.isSubmitable = function()
		{
			return this.lastError == this.ERROR_NO_ERROR || this.lastError == this.DATE_FORMAT_MISSING_VALUE;
		}

		/**
		 * This beautify function is required by the initiating framework.
		 * It takes a String and formats it and returns the beautified string.
		 * 
		 * The return value is actually an array {beautified value, error no, array of format message arguments}
		 *  the error no must have a corresponding entry in the error texts
		 */
		DateFormatter.prototype.formatText = function( rawData )
		{
			var retArr;
			this.lastError = this.ERROR_NO_ERROR;
			
			if ( (rawData = trim( rawData )).length > 0 )
			{ 
				/*
				var format = args.formatStr;
				if ( format == null )
				{
					if ( (format=args[0]) == null )
					{
						return new Array ( rawData, this.lastError = this.DATE_FORMAT_INVALID_FORMATTER );
					}
				}
				*/
				format = this.formatStr;
				/*
				// This is a very simple arser working only on date and not time!
				// expecting only mdyM
				
				for ( i = 0; i < format.length; i++ )
				{
					
					if ( 
				}
				*/
				
				// this is even simplier:  just US format
				var startFormat = 0;
				var startVal = 0;
				var day;
				var month;
				var year;
				var fmtSplits = [];
				while ( startFormat >= 0 )
				{
					var indexFormat = format.indexOf( "/", startFormat );
					// this only works for the simplification
					if ( indexFormat == -1 )
					{
						var tokenFormat = format.substring( startFormat );
					}
					else
					{
						var tokenFormat = format.substring( startFormat, indexFormat );
					}
					
					if ( indexFormat == -1 )
					{
						// if the format does not have any more delimeter take here also all the rest 
						var tokenVal = rawData.substring( startVal );
					}
					else
					{
						var indexVal = rawData.indexOf( "/", startVal );
						if ( indexVal == -1 )
						{
							return new Array( rawData, this.lastError = this.DATE_FORMAT_TOO_SHORT );
						}
						var tokenVal = rawData.substring( startVal, indexVal );
					}
					
					if ( (tokenVal = trim( tokenVal ) ).length == 0 )
					{
						return new Array ( rawData, this.lastError = this.DATE_FORMAT_TOO_SHORT ); 
					}
					
					// now try to convert the string into a number and assign it to the variables
					var val;
					try
					{
						// this parseInt is not the gratest!
						val = myParseInt( tokenVal );
						switch ( tokenFormat.charAt( 0 ) )
						{
							case 'D' :
							case 'd' :
								day = val;
								fmtSplits[fmtSplits.length] = tokenFormat;
								break;
							case 'M' :
							case 'm' :
								month = val;
								fmtSplits[fmtSplits.length] = tokenFormat;
								break;
							case 'Y' :
							case 'y' :
								year = val;
								fmtSplits[fmtSplits.length] = tokenFormat;
								break;
							default:
								// alert( "tokenizing the format string showed: " + tokenFormat.charAt( 0 ) );
								return new Array( rawData, this.lastError = this.DATE_FORMAT_INVALID_FORMATTER );
						}
					}
					catch (ex )
					{
						switch ( tokenFormat.charAt( 0 ) )
						{
							case 'D' :
							case 'd' :
								return new Array( rawData, this.lastError = this.DATE_FORMAT_EXTRA_CHAR );
							case 'M' :
							case 'm' :
								return new Array( rawData, this.lastError = this.DATE_FORMAT_EXTRA_CHAR );
							case 'Y' :
							case 'y' :
								return new Array( rawData, this.lastError = this.DATE_FORMAT_EXTRA_CHAR );
							default:
								// alert( "what happened" + tokenFormat.charAt( 0 ) );
								return new Array( rawData, this.lastError = 99 );
						}
					}
					startFormat = indexFormat + (indexFormat == -1 ? 0 : 1);
					startVal = indexVal + 1;
				}
				// so now here we should have technical correct three digits!
				if ( month < 1 || month > 12 )
				{
					return new Array( rawData, this.lastError = this.DATE_FORMAT_INVALID_MONTH ); 
				}
				// now find out how many days there are in this month/year
				// get a date object that points to the 1st of next month at midnight
				var date = new Date( year, month, 1, 0, 0, 0 );
				date.setTime( date.getTime() - (1000 * 60 * 60 ) );
				if ( day < 0 || day > date.getDate() )
				{
					return new Array( rawData, this.lastError = this.DATE_FORMAT_INVALID_DAY, new Array( new String( date.getDate() ) ) ); 
				}
				
				var wasLess100 = false;
				if ( year < 100 )
				{
					wasLess100 = true;
					// when the user specifies only as two digits year, lets
					// assume it is either up to five years in the furture 
					// or it is 95 in the past.  
					year += 1900;
					if ( year + 95 < new Date().getFullYear() )
					{
						year += 100;
					}
				}
				else
				{
					// no one will specify never ever a date before this!!!!
					if ( year < 1900 )
					{
						return new Array( rawData, this.lastError = this.DATE_FORMAT_INVALID_YEAR );
					}
				}
				
				// alright, looke like we have a vaild date.  Now see if it it in the range
				// if we have a range error we keep going.  We have a proper Date,
				// it just doesn't match the min/max date
				if ( 'min' in this.args && new Date( year, month, day, 24, 0, 0 ).getTime() < this.args.min.getTime() )
				{
					this.lastError = this.DATE_FORMAT_DATE_TOO_SMALL;
					retArr = [this.__formatIt( this.args.min, fmtSplits )];
				}

				date = new Date( year, month - 1, day, 0, 0, 0 );
				if ( (this.lastError == this.ERROR_NO_ERROR) && ('max' in this.args) )
				{
					if ( date.getTime() > this.args.max.getTime() )
					{
						if ( wasLess100 )
						{
							date.setYear( date.getFullYear() - 100 );
						}
						if ( date.getTime() > this.args.max.getTime() )
						{
							this.lastError = this.DATE_FORMAT_DATE_TOO_BIG;
							retArr = [this.__formatIt( this.args.max, fmtSplits )];
						}
					}
				}
				// I can't think of anything else to nag about, let's format it nicely
				rawData = this.__formatIt( date, fmtSplits );
			}
			if ( rawData == "" && this.args.required )
			{
				this.lastError = this.DATE_FORMAT_MISSING_VALUE;
			}
			return new Array( rawData, this.lastError, retArr );
		}

		DateFormatter.prototype.__df_initialized = null;
	}
}

DateFormatter.prototype = new Beautifier();



/*=======================================================
 *  this function ensures that, incase the user name gets changed in 
 *  edit profile that the password is required too.
 *=======================================================*/
/*****
 * args.fieldName    : String : mandatory: FieldName where this validator works on
 * args.originalName : String : mandatory: initial value of the field
 * args.required     : Boolean: optional:  flag if this field is a must (here mot likely always true)
 * args.ignoreCase   : Boolean: optional:  flag if the username is to be treated case sensetive or insensetive
 * args.callBack     : function : optional:  called after the validation completed
 * args.illegal      : String : optional:   String with not allowed characters.  Default: "*'?+"<>"
 * args.minLength    : Number : optional:   minimum string length of the value.  Defaukt: 5
 * args.maxLength    : Number : optional:   maximum string length of the value.  Defaukt: 322
 */
function UserNameMonitor( args )
{
	Beautifier.call( this, args.fieldName );

	this.args = args;
	this.ignoreCase = new Boolean( this.args.ignoreCase );

	this.originalName = (this.ignoreCase ?  this.args.originalName.toLowerCase() : this.args.originalName );

	this.illegal = "*'?+\"<>";
	if ( "illegal" in args )
	{
		this.illeagal = args.illegal;
	}
	
	this.minLength = 5;
	if ( "minLength" in args && typeof args.minLength == "Number" )
	{
		this.minLength = args.minLength;
	}

	this.maxLength = 322;
	if ( "maxLength" in args && typeof args.maxLength == "Number" )
	{
		this.maxLength = args.maxLength;
	}
	
	if ( typeof UserNameMonitor.prototype.__unm_initializer == "undefined" )
	{
		//  This is the array witth error texts that is required buy the initiating
		//  framework.  The numbers must match the number returned by the formatText 
		//  function.
		UserNameMonitor.prototype.ERROR_MISSING_VALUE = 1;
		UserNameMonitor.prototype.ERROR_USER_NAME_CHANGED = 2;
		UserNameMonitor.prototype.ERROR_ILLEGAL_CHAR  = 3;
		UserNameMonitor.prototype.ERROR_TOO_LONG      = 4;
		UserNameMonitor.prototype.ERROR_TOO_SHORT     = 5;
	
		UserNameMonitor.prototype.errorTexts     = new Array();
		this.errorTexts[this.ERROR_MISSING_VALUE    ] = BeautifierErrorText["forms.profile.missUserName"];
		this.errorTexts[this.ERROR_USER_NAME_CHANGED] = BeautifierErrorText["forms.profile.reenterPwd"];
		this.errorTexts[this.ERROR_ILLEGAL_CHAR     ] = BeautifierErrorText["forms.profile.userNameIllegalCharacter"];
		this.errorTexts[this.ERROR_TOO_LONG         ] = BeautifierErrorText["forms.profile.userNameIsTooLong"];
		this.errorTexts[this.ERROR_TOO_SHORT        ] = BeautifierErrorText["forms.profile.userNameIsTooShort"];
		
		/**
		 * length errors will be captured from the backend
		 */
		UserNameMonitor.prototype.isSubmitable = function()
		{
			return this.lastError != this.ERROR_ILLEGAL_CHAR;
			// this.lastError == this.ERROR_NO_ERROR || this.lastError == this.DATE_FORMAT_MISSING_VALUE;
		}

		/**
		 * This beautify function is required by the initiating framework.
		 * It takes a String and formats it and returns the beautified string.
		 * 
		 * The return value is actually an array {beautified value, error no, array of format message arguments}
		 *  the error no must have a corresponding entry in the error texts
		 */
		UserNameMonitor.prototype.formatText = function( rawData )
		{
			var errArgs = null;
			this.lastError = this.ERROR_NO_ERROR;
			
			if ( (rawData = trim( rawData )).length == 0 )
			{
				if ( this.args.required )
				{
					error = this.ERROR_MISSING_VALUE;
				}
			}
			else
			{
				if ( this.ignoreCase )
				{
					rawData = rawData.toLowerCase();
				}
				for ( var i =  0; i < rawData.length; i++ )
				{
					if ( this.illegal.indexOf( rawData.charAt( i ) ) > -1 )
					{
						this.lastError = this.ERROR_ILLEGAL_CHAR;
						errArgs = [rawData.charAt( i )];
						break;
					}
				}
				if ( this.lastError == this.ERROR_NO_ERROR && rawData.length < this.minLength )
				{
					errArgs = [this.minLength];
					this.lastError = this.ERROR_TOO_SHORT;
				}
				if ( this.lastError == this.ERROR_NO_ERROR && rawData.length > this.maxLength )
				{
					errArgs = [this.maxLength];
					this.lastError = this.ERROR_TOO_LONG;
				}
				if ( this.lastError == this.ERROR_NO_ERROR && this.originalName != rawData )
				{
					this.lastError = this.ERROR_USER_NAME_CHANGED;
				}
			}
			
			if ( 'callBack' in this.args )
			{
				this.args.callBack( rawData, this.lastError, this );
			}
			return new Array( rawData, this.lastError, errArgs );
		}
		
		UserNameMonitor.prototype.__unm_initializer = true;
	}
}

UserNameMonitor.prototype = new Beautifier();

/*=======================================================
 * This is a the beautification framework:
 =======================================================*/
/*
 * This function used by the validation framework to show or hide 
 * the error text error treatment, detailing the HTML persentation
 * Setting the label to read and showing error texts
 */
function handleIsInputFieldErrors( fieldName, errorNo, errorText )
{
	field = document.getElementById( "Label_" + fieldName );
	if ( field != null )
	{
		if ( errorNo == 0 )
		{
			field.className = "";
		}
		else
		{
			field.className = "warning";
		}
	}
	field = document.getElementById( "Error_" + fieldName );
	if ( field != null )
	{
		if ( errorNo == 0 )
		{
			field.style.display = "none";
		}
		else
		{
			field.style.display = "";
			field.innerHTML = errorText;
		}
	}
}


/**
 * this function does all the initialization for a field with a beautifier...
 *  The only agument is an object with the following requirement:
 *    String getFieldName()
 *    Object[] formatText( String rawText )
 *        ret[0]:  mandatory: String: beautified text
 *        ret[1]:  mandatory: Number: error number
 *        ret[2]:  optional : array of objects used for messages with {n}
 *   errorTexts:  array of String holding the error texts.  The error # must match the array index. 
 */
function beautifyField( beautifier )
{
	var field;
	
	field = document.getElementsByName( beautifier.getFieldName() );
		
	if ( field != null && field.length == 0 )
	{
		field == null;
	}
	else
	{
		field = field[0];
	}
	
	if ( field != null )
	{
		field.onblur = function( event )
				{
					try
					{
						var ret = beautifier.formatText( field.value );
						field.value = ret[0];
						
						var errTxt = beautifier.errorTexts[ret[1]];
						if ( ret.length > 2 && ret[2] != null )
						{
							errTxt = formatMessage( errTxt, ret[2] );
						}
						handleIsInputFieldErrors( beautifier.getFieldName() , ret[1], errTxt ); 
					}
					catch ( ex )
					{
						handleIsInputFieldErrors( beautifier.getFieldName(), 99, "Error processing the JavaScript validation!" ); 
					}
				};
		field.value = beautifier.formatText( field.value )[0];
	}
}

function runBeautifier( beautifier )
{
	var field;
	
	field = document.getElementsByName( beautifier.getFieldName() );
		
	if ( field != null && field.length == 0 )
	{
		field == null;
	}
	else
	{
		field = field[0];
	}
	if ( field != null )
	{
		field.onblur();
	}
}


var __listOfValidator = new Array()

function addValidator( validator )
{
	if ( __listOfValidator == null )
		__listOfValidator = new Array();
	__listOfValidator[__listOfValidator.length] = validator;
}

/*
 *  this function allows submitting only with proper values
 */
function isSubmitable( )
{
	var ret = true;
	if ( __listOfValidator != null )
	{
		for ( var i = 0; i < __listOfValidator.length; i++ )
		{
			// this is just to make sure we executed the validation
			// with error high beautifier once
			runBeautifier( __listOfValidator[i] );
			
			ret = ret && __listOfValidator[i].isSubmitable() ;
			// alert( i + "  " + __listOfValidator[i].lastError + "   " + ret );
		}
	}
	return ret;
}
