function Form( htmlElement )
{				
	// Connect object to element
	this.mElement = htmlElement;
	htmlElement.form = this;
	
	// Initialize properties
	this.mFieldLength = 0;
	this.mFields = new Object();
	this.mFieldList = new Array();
	var fieldElements = htmlElement.elements;
	var fieldElementsLength = fieldElements.length;
	var fieldElement = null;
	var fieldName = null;
	var field = null;
	var fieldType = null;
	for( var i=0; i < fieldElementsLength; i++ )
	{
		// Check whether field already exists
		fieldElement = fieldElements[i];
		fieldName = this.GetFieldName( fieldElement );
		if ( fieldName == null ) // Skip field is fieldname is not valid
			continue;
			
		field = this.mFields[ fieldName ];
		if ( field == null )
		{
			fieldType = fieldElement.binding ? fieldElement.binding : fieldElement.type;
			// Create new Formfield object and attach it to the element
			switch( fieldType )
			{
				case 'formula':
					field = new FormulaField( fieldElement, this );
					break;
				case 'radio':
					field = new RadioField( fieldElement, this );
					break;
				default:
					field = new FormField( fieldElement, this );
					break;
			}

			this.mFields[ fieldName ] = field;
			this.mFieldList.push( field );
			this.mFieldLength++;
		}
		else if ( fieldElement.type == 'radio' )
		{
			// Only radio fields can have the same name
			fieldElement.field = field;
			
			// Create list of radio elements, push first element on list
			if ( field.mElements == null )
			{
				field.mElements = new Array();
				field.mElements.push( field.mElement );
			}
			field.mElements.push( fieldElement );
		}
		else
		{
			alert( 'FormFields cannot have the same name: ' + fieldElement.name );
		}
	}

	// Set condition handlers and dependencies and hide the fields
	var targetField = null;
	for( var i=0; i < this.mFieldLength; i++ )
	{	
		field = this.mFieldList[ i ];
		// Set condition dependencies
		if ( field.mConditionField !== null )
		{
			targetField = this.GetField( field.mConditionField );
			if ( targetField != null )
			{
				targetField.AddDependentElement( field );
				field.HandleChange( field.mConditionField, targetField.GetValue() ); 
			}
		}
		
		// Set formula dependencies
		if ( field.constructor == FormulaField )
			field.SetDependencies();
	}			
}

// Method: GetField
Form.prototype.GetField = _Form_GetField;
function _Form_GetField( fieldName )
{
	if ( IsEmpty( fieldName ) )
		return null;

	return this.mFields[ fieldName.toLowerCase() ];
}

// Method: GetField
Form.prototype.GetFieldName = _Form_GetFieldName;
function _Form_GetFieldName( fieldElement )
{
	if( fieldElement.name.indexOf( 'PostedField[' ) == 0 )
		return fieldElement.name.substring( fieldElement.name.indexOf( '[' ) + 1, fieldElement.name.indexOf( ']' ) ).toLowerCase();
	else
		return null;
}


// Class: formfield
function FormField( htmlElement, formObject )
{
	// Initialize element
	if ( htmlElement != null )
		this.Initialize( htmlElement, formObject );
}

// Method: Initialize
FormField.prototype.Initialize = _FormField_Initialize;
function _FormField_Initialize( htmlElement, formObject )
{
	// Initialize properties
	this.mForm = formObject;
	this.mType = htmlElement.type;
	this.mElement = htmlElement;
	
	// Set reference in htmlElement to this field
	htmlElement.field = this;
	
	// Set name, label, etc
	var attributes = htmlElement.attributes;
	this.mName = this.mForm.GetFieldName( htmlElement );
	this.mId = attributes.getNamedItem( 'id' ).value; 
	this.mId = this.mId.substring( this.mId.indexOf( '[' ) + 1, this.mId.indexOf( ']' ) ); // extract id from FormField[id]

	if ( attributes.getNamedItem( 'label' ) )
		this.mLabel = attributes.getNamedItem( 'label' ).value;
	
	// Check required
	this.mRequired = false;	
	if ( IsTrue( htmlElement.required ) )
		this.mRequired = true;
		
	// Set enabled property
	this.mIsEnabled = true;

	// Check conditions
	if ( attributes.getNamedItem( 'condition' ) != null )
	{
		var condition = attributes.getNamedItem( 'condition' ).value;
		if ( ! IsEmpty( condition ) )
		{
			var equalPos = condition.indexOf( '=' );
			if ( equalPos > -1 )
			{
				var conditions = ParseQueryString( condition );
				for( var key in conditions )
				{
					if ( key == null )
						break;
					
					if ( conditions[ key ] == null )
						break;
					
					this.mConditionValues = conditions[ key ].split( '|' );
					this.mConditionField = key.toLowerCase();		 
				}
			}
		}
	}
}

// Method: AddDependentElement 
FormField.prototype.AddDependentElement = _FormField_AddDependentElement;
function _FormField_AddDependentElement( element ) 
{
	if ( this.mDependentElements == null )
		this.mDependentElements = new Array();

	this.mDependentElements.push( element );
}

// Method: Display
FormField.prototype.DisplayField = _FormField_DisplayField;
function _FormField_DisplayField()
{
	var htmlElement = document.getElementById( 'FieldContainer-' + this.mId );
	if ( htmlElement )
	{
		// Make lighter in backoffice mode
		if ( top.applicationUrl == null )
			htmlElement.style.display = '';
			
		this.mElement.disabled = false;
	}
}

// Method: GetValue
FormField.prototype.GetValue = _FormField_GetValue;
function _FormField_GetValue()
{
	return this.mElement.value;
}

// Method: Handle change Event
// If field is conditional, check condition values
FormField.prototype.HandleChange = _FormField_HandleChange;
function _FormField_HandleChange( fieldName, fieldValue )
{
	// Check fieldName
	if ( this.mConditionField != fieldName )
		return;
		
	// Check condition values
	if ( this.mConditionValues == null )
		return;
	
	var boolConditionMet = false;
	if ( this.mConditionValues[ 0 ] == "not_empty" )
	{
		boolConditionMet = ! IsEmpty( fieldValue );
	}
	else
	{
		var length = this.mConditionValues.length;
		for( var i=0; i < length; i++ )
		{
			if ( this.mConditionValues[ i ] == fieldValue )
			{
				boolConditionMet = true;
				break;
			}
		}
	}
	
	// if no conditionvalue is found, disabled field
	if ( boolConditionMet )
	{
		this.mIsEnabled = true;
		this.DisplayField();
	}
	else
	{
		this.mIsEnabled = false;
		this.HideField();
	}
}

// Method: Hide
FormField.prototype.HideField = _FormField_HideField;
function _FormField_HideField()
{
	var htmlElement = document.getElementById( 'FieldContainer-' + this.mId );
	if ( htmlElement )
	{
		// Make lighter in backoffice mode
		if ( top.applicationUrl == null )
			htmlElement.style.display = 'none';
			
		this.mElement.disabled = true;
	}
}

// Method: OnChange Event
FormField.prototype.OnChange = _FormField_OnChange;
function _FormField_OnChange()
{
	if( this.mDependentElements == null )
		return;
		
	var length = this.mDependentElements.length;
	for ( var i=0; i < length; i++ )
	{
		this.mDependentElements[ i ].HandleChange( this.mName, this.GetValue() );		
	}	
}

// Method: GetValue
FormField.prototype.SetValue = _FormField_SetValue;
function _FormField_SetValue( value )
{
	this.mElement.value = value;
}

// Specific FormFields --------------------------------------------------------

// Class: CheckboxField 
function CheckboxField( htmlElement, formObject )
{
	this.Initialize( htmlElement, formObject );
}

// Extend: Field
CheckboxField.prototype = new FormField();
CheckboxField.prototype.constructor = CheckboxField;

// Method: get checkbox value
CheckboxField.prototype.GetValue = _CheckboxField_GetValue;
function _CheckboxField_GetValue()
{
	// Get value
	if ( this.checked == true )
		return '1';
	else
		return '0';
}

// Method: GetValue
CheckboxField.prototype.SetValue = _CheckboxField_SetValue;
function _CheckboxField_SetValue( value )
{
	this.checked = IsTrue( value );
}

// Class: FormulaField 
function FormulaField( htmlElement, formObject )
{
	// Initialize field
	this.Initialize( htmlElement, formObject );
		
	// Initiaze properties
	this.mFormulaFields = new Object();
	this.mFormula = htmlElement.formula;
	this.mNumberFormat = htmlElement.numberFormat;	
}

// Extend: Field
FormulaField.prototype = new FormField();
FormulaField.prototype.constructor = FormulaField;

// Method: GetNumberValueOf fieldName
FormulaField.prototype.SetDependencies = _FormulaField_SetDependencies
function _FormulaField_SetDependencies( fieldName )
{
	if ( ! IsEmpty( this.mFormula ) )
	{
		// eval formula to get find the used fields
		eval( this.mFormula.replace( /veld\(/g, 'this.MakeFieldDependentOn(' ) );
	}
}

// Method: GetNumberValueOf fieldName
FormulaField.prototype.GetNumberValueOf = _FormulaField_GetNumberValueOf
function _FormulaField_GetNumberValueOf( fieldName )
{
	var targetField = this.mForm.GetField( fieldName );
	if ( targetField == null )
	{
		alert( 'Veld ' + fieldName + ' bestaat niet' );
		return null;
	}

	var value = targetField.GetValue();
	
	if ( targetField.mElement.fieldType == 'date' )
   	return value;
	
	// Make english notation
	if ( ! IsEmpty( value ) )
		value =  parseFloat( value.replace( /\./g , '' ).replace( /\,/g, '.' ) );

	if ( isNaN( value ) )
		value = 0;
	
	return value;
}

// Method: Handle change Event
// If field is conditional, check condition values
FormulaField.prototype.HandleChange = _FormulaField_HandleChange;
FormulaField.prototype._base_HandleChange = _FormField_HandleChange;
function _FormulaField_HandleChange( fieldName, fieldValue )
{  
	// Execute base handle change
	this._base_HandleChange( fieldName, fieldValue );

	if ( this.mFormulaFields[ fieldName ] != null )
	{
		var result = eval( this.mFormula.replace( /veld\(/g, 'this.GetNumberValueOf(' ) );
		
		// Format result
		switch( this.mNumberFormat )
		{
			case 'rounded':
				result = Math.round( result );
				break;
			case 'currencyNL':
				result = FormatCurrency( result, 'NL' );
				break;
			case 'currencyEN':
				result = FormatCurrency( result, 'EN' );
				break;
			case 'NL':
				result = result.toString().replace( /\./g, ',' );
				break;
			case 'EN':
			default:
				// do nothing
				break;				
		}
		this.SetValue( result );
	}
}

// Method: get value on radio
FormulaField.prototype.MakeFieldDependentOn = _FormulaField_MakeFieldDependentOn;
function _FormulaField_MakeFieldDependentOn( fieldName )
{
	// Name check
	if ( IsEmpty ( fieldName ) )
		return;
		
	// Only add dependency once
	fieldName = fieldName.toLowerCase();
	var targetField = this.mForm.GetField( fieldName );
	if ( this.mFormulaFields[ fieldName ] == null &&  targetField != null )
	{
		targetField.AddDependentElement( this );
		this.mFormulaFields[ fieldName ] = targetField;
	}
}

// Method: GetValue
FormulaField.prototype.SetValue = _FormulaField_SetValue;
function _FormulaField_SetValue( value )
{
	this.mElement.value = value;
	this.OnChange();
}

// Class: RadioField 
function RadioField( htmlElement, formObject )
{
	this.Initialize( htmlElement, formObject );
}

// Extend: Field
RadioField.prototype = new FormField();
RadioField.prototype.constructor = RadioField;

// Method: get value on radio
RadioField.prototype.GetValue = _RadioField_GetValue;
function _RadioField_GetValue()
{
   // Initialize found
   var found = false;
   
   // Get html element, get number of options
   var radioOptions = this.mElements;
   var length = radioOptions.length;

   // Loop over all select options
   var i;
   for ( i = 0; i < length; i++ ) 
   {
      if ( radioOptions[ i ].checked == true ) 
      {
         return radioOptions[ i ].value;
      }
   }
   
   // Else return empty string, because initialValue is also an empty string, if not set.
   return "";
}

// Method: get value on radio
RadioField.prototype.SetValue = _RadioField_SetValue;
function _RadioField_SetValue( value )
{
   // Initialize found
   var found = false;
   
   // Get html element, get number of options
   var radioOptions = this.mElements;
   var length = radioOptions.length;

   // Loop over all select options
   var i;
   for ( i = 0; i < length; i++ ) 
   {
      if ( radioOptions[ i ].value == value ) 
      {
         radioOptions[ i ].checked = true;
         break;
      }
   }   
}

// Helper functions -----------------------------------------------------------

function FormatCurrency( number )
{
	if ( isNaN( number ) )
		return number;
		
	// Format with 2 decimals
	number = Math.round( number * 100 )/100;
	var numberString = number.toString();
	switch( numberString.indexOf( '.' )  )
	{
		case -1:
			numberString += '.00';
			break;
		case ( numberString.length - 2 ):
			numberString += '0';
			break;
	}
	
	numberString = numberString.replace( '.', ',' );
	
	// Format with thousands separator
	if ( number >= 1000 )
	{
		var startPos = numberString.indexOf( ',' ) - 3;
		var newNumberString = numberString.substr( numberString.length - 6 );
		while( startPos > 3 )
		{
			newNumberString = numberString.substr( startPos - 3, 3 ) + '.' + newNumberString;
			startPos -= 3;	
		}
		numberString = numberString.substr( 0, startPos ) + '.' + newNumberString;
	}
	
	return numberString;
}

// Is Empty Check
function IsEmpty( input ) 
{
	return ( input == null || input == '' );
}

// Check if value represents true
function IsTrue( value )
{
	// Set value to lowercase
	if ( typeof( value ) == 'string' )
	{
		value = value.toLowerCase();
	}
	
	// Set value
	return ( value == true 
			|| value == '1' 
			|| value == 'true' 
			|| value == 'yes'
			|| value == 'on' );
} 

// Return the query string as an assosiative array
function ParseQueryString( queryString )
{
	// Initialize variables
	var parameters = new Array();

	// Null check
	if ( queryString == null )
		return parameters;
		
	// strip ? from querystring
	var pos = queryString.indexOf('?');
	if ( pos > -1 )
		queryString = queryString.substr(pos+1);
	
	// Replace + by spaces ( decodeURI does not do that )	
	queryString = queryString.replace( /\+/g, ' ' );

	// Get parameters
	var keyValuePairs = queryString.split( '&' );
	var equalPos = -1;
	var parameter;
	for ( var i in keyValuePairs )
	{
	   // Split on =
	   parameter = keyValuePairs[ i ];
	   equalPos = parameter.indexOf( '=' );
	   if ( equalPos > -1 )
	   	parameters[ decodeURIComponent( parameter.substr( 0, equalPos ) ) ] = decodeURIComponent( parameter.substr( equalPos + 1 ) );
	}
	
	// Return parameters
	return parameters;
}

function OnChangePickList( element )
{
   var optionGroup = null;
   var selectGroup = false;
   var option = null;

   // Loop door alle options van deze select heen
   for ( var i = 0; i < element.children.length; i++ )
   {
      // Als de eerste karakters van de optie gelijk zijn aan de indent, dan gaat het hier om een subniveau
      if ( element.children[ i ].innerText.substring( 0, element.indent.length ) == element.indent )
      {
         // Als er een optionGroup geladen is... 
         if ( optionGroup != null )
         {
            // ... dan controleren of een van de onderliggende items geselecteerd is.
            if ( element.children[ i ].selected == true )
               selectGroup = true;
         }
      }
      else
      {
         // Selecteer de optionGroup indien een van de onderliggende items geselecteerd is, anders deselecteren
         if ( optionGroup != null )
            optionGroup.selected = selectGroup;
         // Stop de volgende optionGroup in de variabele
         optionGroup = element.children[ i ];

         selectGroup = element.children[ i ].selected;
      }
   }
}



// FormulaField functions

function DateAdd( date, value, period )
{
   if ( date == null || date == '' || value == null || value == '' || period == null || period == '' )
      return '';

	var millisecond = 1;
	var second = millisecond * 1000;
	var minute = second * 60;
	var hour = minute * 60;
	var day = hour * 24;
	var week = day * 7;
	var year = day * 365;

   var dateDay = date.substring( 0, 2 );
   var dateMonth = date.substring( 3, 5 );
   var dateYear = date.substring( 6, 10 );

	var oldDate = new Date();
   // fix if string begins with "0x" (the radix is 16 hexadecimal)
   oldDate.setDate( parseInt( dateDay, 10 ) );
   oldDate.setMonth( dateMonth -1, 10 );
	oldDate.setFullYear( parseInt( dateYear ) );
	var oldDateValue = oldDate.valueOf();

	var newDate;

	switch( period )
	{
		case "ms":
		   newDate = new Date( oldDateValue + ( millisecond * value ));
		   break;
		case "s":
		   newDate = new Date( oldDateValue + ( second * value ));
		   break;
		case "mi":
		   newDate = new Date( oldDateValue + ( minute * value ));
		   break;
		case "h":
		   newDate = new Date( oldDateValue + ( hour * value ));
		   break;
		case "d":
	   	newDate = new Date( oldDateValue + ( day * value ));
   		break;
		case "w":
	   	newDate = new Date( oldDateValue + ( week * value ));
   		break;
		case "y":
		   newDate = new Date( dateValue + ( year * value ));
		   break;
	}

   if ( newDate == null )
      return '';
      
   return ConstructDate( newDate.getDate(), newDate.getMonth(), newDate.getFullYear(), 'dd-MM-yyyy' );
}

function ConstructDate( d, m, y, dateFormat )
{
   var sTmp = dateFormat;
   sTmp = sTmp.replace( "dd","<e>");
   sTmp = sTmp.replace( "d","<d>");
   sTmp = sTmp.replace( "<e>",padZero(d));
   sTmp = sTmp.replace( "<d>",d);
   sTmp = sTmp.replace( "MMMM","<o>");
   sTmp = sTmp.replace( "MMM","<p>");
   sTmp = sTmp.replace( "MM","<n>");
   sTmp = sTmp.replace( "M","<m>");
   sTmp = sTmp.replace( "<m>",m+1);
   sTmp = sTmp.replace( "<n>",padZero(m+1));
   sTmp = sTmp.replace( "<o>",monthName[m]);
   sTmp = sTmp.replace( "<p>",monthNameShort[m]);
   sTmp = sTmp.replace( "yyyy", y );
   return sTmp.replace ("yy",y%100);
}