/**
	* Validation functions i this file are inspired by and in some cases 
	* modified versions of apaches struts/commons validator javascript files.
	*/

/**	
    * Check to see if field is a valid byte.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateByteField(field, params) {
	var bValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'select-one' || 
			field.type == 'radio')) {

			var value = '';
			// get field's value
			if (field.type == "select-one") {
				var si = field.selectedIndex;
				if (si >= 0) {
					value = field.options[si].value;
				}
			} else {
				value = field.value;
			}

			if (value.length > 0) {
				if (!jcv_isDecimalDigits(value)) {
					bValid = false;
				} else {
					var iValue = parseInt(value, 10);
					if (isNaN(iValue) || !(iValue >= -128 && iValue <= 127)) {
						bValid = false;
					}
				}
			}

		}
	}
	return bValid;
}

/**
    * Check to see if field is a valid creditcard number.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateCreditCardField(field, params) {
	var bValid = true;
	if (field) {
		if ((field.type == 'text' || field.type == 'textarea') && (field.value.length > 0)) {
			if (!jcv_luhnCheck(field.value)) {
				bValid = false;
			}
		}
	}
	return bValid;
}

/**
    * Check to see if field is a String.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIsStringField(field, params) {
	var bValid = true;
	var stringRegExp = "^[ -~\u00a1-\u00ff]*$";
	var stringRegExp_FR = "^[ -/:-~ÀÈÉÊËÎÏÙÔàâçèéêëîïôùû]*$";
	if (field) {
		var countryCode = params.get("defaultCountry");
		if (fieldValue && fieldValue.length > 0) {
			if ("FR" == countryCode) {
				stringRegExp = stringRegExp_FR;
			}
			if (!fieldValue.match(stringRegExp)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if field is a valid date.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateDateFormatField(field, params) {
	var bValid = true;
	var value = field.value;
	/*
	var isStrictStr = params.get("isStrict");
	var isStrict = true;
	if (isStrictStr != null && "false" == isStrictStr) {
		isStrict = false;
	}
	// try loose pattern
	var datePatterns = params.get("dateFormat");
	*/
	var isStrict = true;
	var datePatterns = params.get("dateFormatStrict");
	// try loose pattern
	if (datePatterns == null) {
		datePatterns = params.get("dateFormat");
		isStrict = false;
	}

	if (field) {
		if ((field.type == 'text' || 
			field.type == 'textarea') && 
			(value.length > 0) && (datePatterns.length > 0)) {

			var patternTokens = datePatterns.split(",");
			for (var i = 0; i < patternTokens.length; i++) {
				bValid = true;
				var datePattern = patternTokens[i];

				var MONTH = "MM";
				var DAY = "dd";
				var YEAR = "yyyy";
				var XXXX = "x";
				var orderMonth = datePattern.indexOf(MONTH);
				var orderDay = datePattern.indexOf(DAY);
				
				if (datePattern.indexOf(XXXX) > -1 || "true" == params.get("personalId")) {
					if (datePattern.indexOf(YEAR) == -1) {
						YEAR = "yy";
						value = value.substring(0,6);
					} else {
						value = value.substring(0,8);
					}
				}
				var orderYear = datePattern.indexOf(YEAR);
				if ((orderDay < orderYear && orderDay > orderMonth)) {
					var iDelim1 = orderMonth + MONTH.length;
					var iDelim2 = orderDay + DAY.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderDay && iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else if (iDelim1 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					} else if (iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (!jcv_isValidDate(matched[2], matched[1], matched[3])) {
							bValid = false;
						}
						break;
					} else {
						bValid = false;
					}
				} else if ((orderMonth < orderYear && orderMonth > orderDay)) {
					var iDelim1 = orderDay + DAY.length;
					var iDelim2 = orderMonth + MONTH.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderMonth && iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else if (iDelim1 == orderMonth) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					} else if (iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (!jcv_isValidDate(matched[1], matched[2], matched[3])) {
							bValid = false;
						}
						break;
					} else {
						bValid = false;
					}
				} else if ((orderMonth > orderYear && orderMonth < orderDay)) {
					var iDelim1 = orderYear + YEAR.length;
					var iDelim2 = orderMonth + MONTH.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderMonth && iDelim2 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})(\\d{2})(\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})(\\d{1,2})(\\d{1,2})$");
					} else if (iDelim1 == orderMonth) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})(\\d{2})[" + delim2 + "](\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})(\\d{1,2})[" + delim2 + "](\\d{1,2})$");
					} else if (iDelim2 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{2})(\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{1,2})(\\d{1,2})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{1,2})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (!jcv_isValidDate(matched[3], matched[2], matched[1])) {
							bValid = false;
						}
						break;
					} else {
						bValid = false;
					}
				} else {
					bValid = false;
				}
			}
		} else if (field && field.type == 'select-one') {
			bValid = validateIsCompleteDateField(field, params);
		}
	}
	return bValid;
}

/**
    * Check to see if field is a valid email address.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateEmailField(field, params) {
	var bValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea') && 
			(field.value.length > 0)) {
			if (!jcv_checkEmail(field.value)) {
				bValid = false;
			}
		}
	}
	return bValid;
}

/**
 * Validates a delimiter separated list of e-mail addresses. 
 * Uses function jcv_checkEmail to validate each individual address.
 * Delimiters to use is set as parameters in validator XML. See 
 * se.ikea.validator.validators.IKEAValidateMultiEmail for further info.
 */
function validateEmailMultipleField(field, params) {
	var bValid = true;
	var chosenDelimiter = null;
	var delimiters = new Array();

	// collect the delimiters
	var i = 0;
	while (true) {
		var param = params.get("delimiter"+(i+1));
		if (!param) {
			break;
		}
		
		delimiters[i]=param;
		i++;

		if (field.value.indexOf(param) != -1) {
			if (chosenDelimiter == null) {
				chosenDelimiter = param;
			}
		}
	}
		
	// do the split
	var ma = field.value.split(chosenDelimiter);

	//Clear the array from empty elements
	var ma_tmp=new Array();
	for(j=0;j<ma.length;j++)
	{
		if(ma[j]!="")
		{
			ma_tmp[ma_tmp.length]=ma[j];
		}
	}
	ma.length=ma_tmp.length;
	for(j=0;j<ma_tmp.length;j++)
	{
		ma[j]=ma_tmp[j];
	}
	
	//Check if we have more addresses than we allow
	var maxNof = params.get("maxNofEmails");
	if (ma.length > maxNof)
		return false;
	
	// check each e-mail address using function jcv_checkEmail
	for (x=0;x<ma.length;x++) {
		if (!jcv_checkEmail(trim(ma[x])))
			return false;
	}
	
	return true;
}

/**
    * Check to see if field is a valid float.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateFloatField(field, params) {
	var bValid = true;

	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'select-one' || 
			field.type == 'radio')) {

			var value = '';
			// get field's value
			if (field.type == "select-one") {
				var si = field.selectedIndex;
				if (si >= 0) {
					value = field.options[si].value;
				}
			} else {
				value = field.value;
			}

			if (value.length > 0) {
				// remove '.' before checking digits
				var tempArray = value.split('.');
				//Strip off leading '0'
				var zeroIndex = 0;
				var joinedString = tempArray.join('');
				while (joinedString.charAt(zeroIndex) == '0') {
					zeroIndex++;
				}
				var noZeroString = joinedString.substring(zeroIndex, joinedString.length);

				if (!jcv_isAllDigits(noZeroString) || tempArray.length > 2) {
					bValid = false;
				} else {
					var iValue = parseFloat(value);
					if (isNaN(iValue)) {
						bValid = false;
					} else {
						var positiveOnly = params.get("positiveOnly");
						if (positiveOnly && positiveOnly == "true" && iValue < 0) {
							bValid = false;
						}
					}
				}
			}
		}
	}
	return bValid;
}

/**
    * Check to see if field is in a valid float range.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateFloatRangeField(field) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea') && 
			(field.value.length > 0)) {

			var fMin = parseFloat(params.get("min"));
			var fMax = parseFloat(params.get("max"));
			var fValue = parseFloat(field.value);
			if (! (fValue >= fMin && fValue <= fMax)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if field is a valid integer.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIntegerField(field, params) {
	var bValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'select-one' || 
			field.type == 'radio')) {

			var value = '';
			// get field's value
			if (field.type == "select-one") {
				var si = field.selectedIndex;
				if (si >= 0) {
					value = field.options[si].value;
				}
			} else {
				value = field.value;
			}

			if (value.length > 0) {

				if (!jcv_isDecimalDigits(value)) {
					bValid = false;
				} else {
					var iValue = parseInt(value, 10);
					var positiveOnly = params.get("positiveOnly");
					var minValue = -2147483648;
					if (positiveOnly && positiveOnly == "true") {
						minValue = 0;
					}
					if (isNaN(iValue) || !(iValue >= minValue && iValue <= 2147483647)) {
						bValid = false;
					}
				}
			}
		}
	}
	return bValid;
}

/**
    * Check to see if field is in a valid int range.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIntRangeField(field, params) {
	var isValid = true;
	var value = '';
	if (field) {
		if (field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'radio') {
			value = field.value;
		}
		if (field.type == 'select-one') {
			var si = field.selectedIndex;
			if (si >= 0) {
				value = field.options[si].value;
			}
		}
		if (value.length > 0) {
			var iMin = parseInt(params.get("min"),10);
			var iMax = parseInt(params.get("max"),10);
			var iValue = parseInt(value, 10);
			if (! (iValue >= iMin && iValue <= iMax)) {
				isValid = false;
			}
		}
	}
	return isValid;
}
/**
    * Check to see if field is valid using a regular expression.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateMaskField(field, params) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'file') && 
			(field.value.length > 0)) {

			if (!jcv_matchPattern(field.value, params.get("mask"))) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    *  Caution: Using validateMaxLengthField() on a password field in a 
    *  login page gives unnecessary information away to hackers. While it only slightly
    *  weakens security, we suggest using it only when modifying a password.
    *
    * Check to see if field is less than the specified maximum.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateMaxLengthField(field, params) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'password' || 
			field.type == 'textarea')) {

			/* Adjust length for carriage returns - see Bug 37962 */
			var lineEndLength = params.get("lineEndLength");
			var adjustAmount = 0;
			if (lineEndLength) {
				var rCount = 0;
				var nCount = 0;
				var crPos = 0;
				while (crPos < field.value.length) {
					var currChar = field.value.charAt(crPos);
					if (currChar == '\r') {
						rCount++;
					}
					if (currChar == '\n') {
						nCount++;
					}
					crPos++;
				}
				var endLength = parseInt(lineEndLength,10);
				adjustAmount = (nCount * endLength) - (rCount + nCount);
			}

			var iMax = parseInt(params.get("maxLength"),10);
			if (trim(field.value).length > 0) {
				if ((field.value.length + adjustAmount) > iMax) {
					isValid = false;
				}
			}
		}
	}
	return isValid;
}

/**
    *  Caution: Using validateMinLengthField() on a password field in a 
    *  login page gives unnecessary information away to hackers. While it only slightly
    *  weakens security, we suggest using it only when modifying a password.
	*
    * Check to see if field is greater than the specified minimum.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */

function validateMinLengthField(field, params) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'password' || 
			field.type == 'textarea')) {

			/* Adjust length for carriage returns - see Bug 37962 */
			var lineEndLength = params.get("lineEndLength");
			var adjustAmount = 0;
			if (lineEndLength) {
				var rCount = 0;
				var nCount = 0;
				var crPos = 0;
				while (crPos < field.value.length) {
					var currChar = field.value.charAt(crPos);
					if (currChar == '\r') {
						rCount++;
					}
					if (currChar == '\n') {
						nCount++;
					}
					crPos++;
				}
				var endLength = parseInt(lineEndLength,10);
				adjustAmount = (nCount * endLength) - (rCount + nCount);
			}

			var iMin = parseInt(params.get("minLength"),10);
			if ((trim(field.value).length > 0) && ((field.value.length + adjustAmount) < iMin)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if field har a value.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */

function validateRequiredField(field, params) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'file' || 
			field.type == 'radio' || 
			field.type == 'checkbox' || 
			field.type == 'select-one' || 
			field.type == 'password')) {

			var value = '';
			// get field's value
			if (field.type == "select-one") {
				var si = field.selectedIndex;
				if (si >= 0) {
					value = field.options[si].value;
				}
			} else if (field.type == 'radio' || field.type == 'checkbox') {
				if (field.checked) {
					value = field.value;
				} else {
					var radioname = field.name;
					var formElements = field.form.getElementsBySelector('input[name='+radioname+']');
				    for (var i=0; i<formElements.length; i++) {
				    	var radio = formElements[i];
		    			if (radio.type == 'radio' && radio.name == radioname && radio.checked) {
			    			value = field.value;
			    			break;
		    			}
			    	}
				}
			} else {
				value = field.value;
			}

			if (trim(value).length == 0) {
				isValid = false;
			}
		} else if (field.type == "select-multiple") {
			var numOptions = field.options.length;
			lastSelected = -1;
			for (loop = numOptions - 1; loop >= 0; loop--) {
				if (field.options[loop].selected) {
					lastSelected = loop;
					value = field.options[loop].value;
					break;
				}
			}
			if (lastSelected < 0 || trim(value).length == 0) {
				isValid = false;
			}
		} else if ((field.length > 0) && (field[0].type == 'radio' || field[0].type == 'checkbox')) {
			isChecked = -1;
			for (loop = 0; loop < field.length; loop++) {
				if (field[loop].checked) {
					isChecked = loop;
					break; // only one needs to be checked
				}
			}
			if (isChecked < 0) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if a field is used then others are required .
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */

function validateOneUsedOtherRequiredField(field, params) {
	var isValid = true;
	var otherFields = params.get("otherFields");
	var fieldTokens = otherFields.split(",");
	if (fieldTokens.length > 0) isValid = false;

	if (!validateRequiredField(field, params)) {
		for (var i = 0; i < fieldTokens.length; i++) {
			var otherField = eval('$(\"' + field.form.name + '_' + fieldTokens[i] + '\")');
			if (!validateRequiredField(otherField, params)) {
				isValid = true;
			} else {
				isValid = false;
				break;
			}
		}
	} else {
		for (var i = 0; i < fieldTokens.length; i++) {
			var otherField = eval('$(\"' + field.form.name + '_' + fieldTokens[i] + '\")');
			if (validateRequiredField(otherField, params)) {
				isValid = true;
			} else {
				isValid = false;
				break;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if a field is a valid short.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateShortField(field, params) {
	var bValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'select-one' || 
			field.type == 'radio')) {

			var value = '';
			// get field's value
			if (field.type == "select-one") {
				var si = field.selectedIndex;
				if (si >= 0) {
					value = field.options[si].value;
				}
			} else {
				value = field.value;
			}

			if (value.length > 0) {
				if (!jcv_isDecimalDigits(value)) {
					bValid = false;
				} else {

					var iValue = parseInt(value, 10);
					var positiveOnly = params.get("positiveOnly");
					var minValue = -32768;
					if (positiveOnly && positiveOnly == "true") {
						minValue = 0;
					}
					if (isNaN(iValue) || !(iValue >= minValue && iValue <= 32767)) {
						bValid = false;
					}
				}
			}
		}
	}
	return bValid;
}

/**
    * Check to see if a field has a value included in a specified list of permitted values.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateInListField(field, params) {
	var isValid = true;
	if (field) {
		var listStr = params.get("inList");
		var listTokens = listStr.split(",");

		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'select-one' || 
			field.type == 'textarea' || 
			field.type == 'file') && 
			(field.value.length > 0)) {

			isValid = false;
			for (var i = 0; i < listTokens.length; i++) {
				if (trim(field.value) == trim(listTokens[i])) {
					isValid = true;
					break;
				}
			}
		} else if (field.type == 'radio') {
				var radioname = field.name;
				var formElements = field.form.getElementsBySelector('input[name='+radioname+']');
			    for (var i=0; i<formElements.length; i++) {
			    	var radio = formElements[i];
		   			if (radio.type == 'radio' && radio.name == radioname && radio.checked) {
						isValid=false;
						for (var j = 0; j < listTokens.length; j++) {
							if (trim(radio.value) == trim(listTokens[j])) {
								isValid = true;
								break;
							}
						}			
		   			}
		    	}
		}
	}
	return isValid;
}

/**
    * Utility to generate complex regular expressinons for UK zip codes
    */
function generateGBRegExp() {
	/** Valid chars for first position. "The letters Q, V and X are not used in the first position" */
	var alphaPos1 = "[A-PR-UWYZ]";
	/** Valid chars for second position. " The letters I, J and Z are not used in the second position" */
	var alphaPos2 = "[A-HK-Y]";
	/** Valid chars for third position. "The only letters to appear in the third position are A, B, C, D, E, F, G, H, J, K, S, T, U and W" */
	var alphaPos3 = "[ABCDEFGHJKSTUW]";
	/** Valid chars for fourth position. "The only letters to appear in the fourth position are A, B, E, H, M, N, P, R, V, W, X and Y" */
	var alphaPos4 = "[ABEHMNPRVWXY]";
	/** Valid chars for inward code (second half). "The second half of the Postcode is always consistent numeric, alpha, alpha format and the letters C, I, K, M, O and V are never used" */
	var validInwardCodeChars = "[ABD-HJLNP-UW-Z]"; // 

	/** The one special case postal code. Someone always has to ruin the party! */
	var specialCode = "GIR 0AA"; // this is an historic post code that doesn't correspond to UK post code standard, but which is still in use

	/** Regexp of the inward code, aka. NAA (N=Numeric, A=Alphabetic) */
	var NAA = "[\\d]" + validInwardCodeChars + validInwardCodeChars;

	// The six generic forms of a postcode
	var AN_NAA = alphaPos1 + "[\\d] " + NAA;
	var ANN_NAA = alphaPos1 + "[\\d][\\d] " + NAA;
	var AAN_NAA = alphaPos1 + alphaPos2 + "[\\d] " + NAA;
	var AANN_NAA = alphaPos1 + alphaPos2 + "[\\d][\\d] " + NAA;
	var ANA_NAA = alphaPos1 + "[\\d]" + alphaPos3 + " " + NAA;
	var AANA_NAA = alphaPos1 + alphaPos2 + "[\\d]" + alphaPos4 + " " + NAA;;

	/** The final concatenated regexp string */
	var finalPostcodeRegExp = "(?:^" + specialCode + "$)|" + "(?:^" + AN_NAA + "$)|" + "(?:^" + ANN_NAA + "$)|" + "(?:^" + AAN_NAA + "$)|" + "(?:^" + AANN_NAA + "$)|" + "(?:^" + ANA_NAA + "$)|" + "(?:^" + AANA_NAA + "$)";

	return finalPostcodeRegExp;
}
/**
	* Utility for retrieving a valid date from a field.
	*/

function retrieveDate(field, params) {
	var theDate;
	var value = field.value;
	var isStrict = true;
	var datePatterns = params.get("dateFormatStrict");
	// try loose pattern
	if (datePatterns == null) {
		datePatterns = params.get("dateFormat");
		isStrict = false;
	}
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea') && 
			(value.length > 0) && 
			(datePatterns.length > 0)) {
			
			var patternTokens = datePatterns.split(",");
			for (var i = 0; i < patternTokens.length; i++) {
				var datePattern = patternTokens[i];
				var MONTH = "MM";
				var DAY = "dd";
				var YEAR = "yyyy";
				var XXXX = "x";
				if (datePattern.indexOf(XXXX) > -1) {
					if (datePattern.indexOf(YEAR) > -1) {
						value = value.substring(0,8);
					} else {
						value = value.substring(0,6);
					}
				}
				var orderMonth = datePattern.indexOf(MONTH);
				var orderDay = datePattern.indexOf(DAY);
				if (datePattern.indexOf(YEAR) == -1) {
					YEAR = "yy";
				}
				var orderYear = datePattern.indexOf(YEAR);
				if ((orderDay < orderYear && orderDay > orderMonth)) {
					var iDelim1 = orderMonth + MONTH.length;
					var iDelim2 = orderDay + DAY.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderDay && iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else if (iDelim1 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					} else if (iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (jcv_isValidDate(matched[2], matched[1], matched[3])) {
							theDate = new Date();
							theDate.setFullYear(convertToFourDigitYear(matched[3]), matched[1] - 1, matched[2]);
							break;
						}
					}
				} else if ((orderMonth < orderYear && orderMonth > orderDay)) {
					var iDelim1 = orderDay + DAY.length;
					var iDelim2 = orderMonth + MONTH.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderMonth && iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else if (iDelim1 == orderMonth) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					} else if (iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (jcv_isValidDate(matched[1], matched[2], matched[3])) {
							theDate = new Date();
							theDate.setFullYear(convertToFourDigitYear(matched[3]), matched[2] - 1, matched[1]);
							break;
						}
					}
				} else if ((orderMonth > orderYear && orderMonth < orderDay)) {
					var iDelim1 = orderYear + YEAR.length;
					var iDelim2 = orderMonth + MONTH.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderMonth && iDelim2 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})(\\d{2})(\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})(\\d{1,2})(\\d{1,2})$");
					} else if (iDelim1 == orderMonth) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})(\\d{2})[" + delim2 + "](\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})(\\d{1,2})[" + delim2 + "](\\d{1,2})$");
					} else if (iDelim2 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{2})(\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{1,2})(\\d{1,2})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{1,2})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (jcv_isValidDate(matched[3], matched[2], matched[1])) {
							theDate = new Date();
							theDate.setFullYear(convertToFourDigitYear(matched[1]), matched[2] - 1, matched[3]);
							break;
						}
					}
				}
			}
		}
	}
	return theDate;
}

/**
    * Check to see if a field value is over a specified age limit.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateAgeLimitField(field, params) {
	var isValid = true;
	var ageLimit = params.get("ageLimit");
	if (field && (field.type == 'text' || field.type == 'textarea')) {
		if (ageLimit) {
			var ageLimitNum = parseInt(ageLimit,10);
			var inputDate = retrieveDate(field, params);			
			if (inputDate) {
//			if (validateDateFormatField(field, params)) {
//			if (isValidDate(field, params)) {			
				var now = new Date();
				if (now > inputDate) {
					var nowYear = now.getFullYear();
					var nowMonth = now.getMonth();
					var nowDay = now.getDate();
					var age;
					var inputYear = inputDate.getFullYear();
					var inputMonth = inputDate.getMonth();
					var inputDay = inputDate.getDate();
					if ((nowMonth > inputMonth) || (nowMonth == inputMonth & nowDay >= inputDay)) {
						age = nowYear - inputYear
					}
					else {
						age = (nowYear - inputYear) - 1
					}
					if (age < ageLimit) {
						isValid = false;
					}
				}
				else
				{
					isValid = false;
				}
			}
		}
	} else if (field && field.type == 'select-one') {
		if (ageLimit) {
			var ageLimitNum = parseInt(ageLimit,10);
			var inputDate = retrieveCompleteDate(field, params);
//			if (validateIsCompleteDateField(field, params)) {
			if (inputDate) {
				var now = new Date();

				if (now > inputDate) {
					var nowYear = now.getFullYear();
					var nowMonth = now.getMonth();
					var nowDay = now.getDate();
					var age;
					var inputYear = inputDate.getFullYear();
					var inputMonth = inputDate.getMonth();
					var inputDay = inputDate.getDate();
					if ((nowMonth > inputMonth) || (nowMonth == inputMonth & nowDay >= inputDay)) {
						age = nowYear - inputYear
					}
					else {
						age = (nowYear - inputYear) - 1
					}
					if (age < ageLimit) {
						isValid = false;
					}
				}
			}
		}
	}
	return isValid;
}

function retrieveCompleteDate(field, params) {
	var dayField = eval('$(\"' + field.form.name + '_' + params.get("dayField") + '\")');
	var monthField = eval('$(\"' + field.form.name + '_' + params.get("monthField") + '\")');
	var yearField = eval('$(\"' + field.form.name + '_' + params.get("yearField") + '\")');
	if ((dayField && dayField.value && dayField.value.length > 0) && 
		(monthField && monthField.value && monthField.value.length > 0) && 
		(yearField && yearField.value && yearField.value.length > 0)) {
	
		var iDay = parseInt(dayField.value,10);
		var iMonth = parseInt(monthField.value,10);
		var iYear = parseInt(yearField.value,10);
		var inputDate = new Date();
		inputDate.setFullYear(iYear, iMonth, iDay);
		return inputDate;
	}
	return;
}

/**
    * Check to see if a field part of a series of select fields evaluates to a correct date .
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIsCompleteDateField(field, params) {
	var dayMap = new Hash();
	dayMap.set("0", "31");
	dayMap.set("1", "28");
	dayMap.set("2", "31");
	dayMap.set("3", "30");
	dayMap.set("4", "31");
	dayMap.set("5", "30");
	dayMap.set("6", "31");
	dayMap.set("7", "31");
	dayMap.set("8", "30");
	dayMap.set("9", "31");
	dayMap.set("10", "30");
	dayMap.set("11", "31");

	var isValid = true;
	var dayField = eval('$(\"' + field.form.name + '_' + params.get("dayField") + '\")');
	var monthField = eval('$(\"' + field.form.name + '_' + params.get("monthField") + '\")');
	var yearField = eval('$(\"' + field.form.name + '_' + params.get("yearField") + '\")');

	if ((dayField && dayField.value && dayField.value.length > 0) && 
		(monthField && monthField.value && monthField.value.length > 0) && 
		(yearField && yearField.value && yearField.value.length > 0)) {
		
		isValid = false;
		var iDay = parseInt(dayField.value,10);
		var iMonth = parseInt(monthField.value,10);
		var iYear = parseInt(yearField.value,10);
		var maxDay = dayMap.get(monthField.value);
		if (isLeapYear(iYear) && iMonth == 1) {
			maxDay++;
		}
		if (iDay > 0 && iDay <= maxDay) {
			isValid = true;
		}
	}
	return isValid;
}

/**
    * Check to see if a field is a valid zip code for a specific country.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIsZipcodeField(field, params) {
	var isValid = true;
	if (field) {
		var zipMap = new Hash();
		zipMap.set("AA", "^\\d{4}$");
		zipMap.set("AD", "^\\d{3}$");
		zipMap.set("AT", "^\\d{4}$");
		zipMap.set("AU", "^\\d{4}$");
		zipMap.set("BE", "^\\d{4}$");
		zipMap.set("BY", "^\\d{6}$");
		zipMap.set("CA", "^[a-zA-Z]\\d[a-zA-Z]\\s?\\d[a-zA-Z]\\d$");
		zipMap.set("CH", "[0-9]{4}");
		zipMap.set("CN", "^\\d{6}$");
		zipMap.set("CY", "^\\d{4}$");
		zipMap.set("CZ", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("DE", "^\\s?\\d\\s?\\d\\s?\\d\\s?\\d\\s?\\d\\s?$");
		zipMap.set("DK", "^\\s?(([0-24-9]\\s?[0-9])|(3\\s?[0-8]))\\s?\\d\\s?\\d\\s?$");
		zipMap.set("ES", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("FI", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("FR", "^( *\\d *){5}$");
		zipMap.set("GB", generateGBRegExp());
		zipMap.set("GR", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("HU", "^\\d{4}$");
		zipMap.set("IS", "^\\d{3}$");
		zipMap.set("IE", "^[a-zA-Z0-9]{0,2}$");
		zipMap.set("IT", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("JP", "^\\d{3}(\\-)\\d{4}$");
		zipMap.set("KW", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("KZ", "^\\d{6}$");
		zipMap.set("LU", "^\\d{4}$");
		zipMap.set("MC", "^\\d{5}$");
		zipMap.set("MY", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("NL", "^\\d{4}$");
		zipMap.set("NO", "^\\d{4}$");
		zipMap.set("PL", "^\\d{2}(\\-)\\d{3}$");
		zipMap.set("RO", "^\\d{6}$");
		zipMap.set("RU", "^\\d{6}$");
		zipMap.set("SA", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("SE", "^\\s?\\d\\s?\\d\\s?\\d\\s?\\d\\s?\\d\\s?$");
		zipMap.set("SG", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("SK", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("TR", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("TW", "^\\d{3}\\s?\\d{2}$");
		zipMap.set("UA", "^\\d{5}$");
		zipMap.set("US", "^\\d{5}$");

		var fieldValue = field.value;
		var countryCode = params.get("defaultCountry");
		var inputFields = field.form.getElements();
		var formCountryCode = irwCurrentFormCountry(field);
		if (zipMap.get(formCountryCode) && zipMap.get(formCountryCode).length > 0) countryCode = formCountryCode;
		var zipRegExp = zipMap.get(countryCode);

		if (fieldValue && fieldValue.length > 0) {
			if ("GB" == countryCode) {
				if (!fieldValue.match(zipRegExp)) {
					isValid = false;
				}
			} else if (!fieldValue.match(zipRegExp)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/* If a form has a field with the name 'country' the value of that field is returned */
function irwCurrentFormCountry(field) {
	try {
		var inputFields = field.form.getElements();
		var countryCode = '';
		for (var i = 0,
		length = inputFields.length; i < length; i++) {
			var oneField = inputFields[i];
			if (oneField.name && oneField.name == 'country') {
				if (oneField.tagName == 'SELECT') {
					countryCode = oneField[oneField.selectedIndex].value;
				}
				if (oneField.tagName == 'INPUT') {
					countryCode = oneField.value;
				}
				break;
			}
		}
		return countryCode;
	} catch(e) {
		return '';
	}
}

/**
    * Check to see if a field has a valid telephone number for a selected country.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIsTelephoneGlobalField(field, params) {
	var isValid = true;
	if (field) {
		var phoneMap = new Hash();
		phoneMap.set("DEFAULT", "^[ 0-9\\+()\\-]*$");
		phoneMap.set("CH", "^[ 0-9\\+()-]{9,20}$");
		phoneMap.set("FR", "^[ 0-9\\+()\\-]{9,20}$");
		var fieldValue = field.value;
		var countryCode = params.get("defaultCountry");
		var phoneRegExp = phoneMap.get("DEFAULT");
		var formCountryCode = irwCurrentFormCountry(field);
		if (phoneMap.get(formCountryCode) && phoneMap.get(formCountryCode).length > 0) countryCode = formCountryCode;
		if (fieldValue && fieldValue.length > 0) {
			if ("CH" == countryCode || "FR" == countryCode) {
				phoneRegExp = phoneMap.get(countryCode);
			}
			if (!fieldValue.match(phoneRegExp)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if a field is a valid IKEA part number.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateIsValidPartNumberField(field, params) {
	var isValid = true;
	if (field) {
		var partNumberRegExp = "^[0-9]{8}$";
		var fieldValue = field.value;
		if (fieldValue && fieldValue.length > 0) {
			if (!fieldValue.match(partNumberRegExp)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if only one of two fields has a value .
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateAtMostOneRequiredField(field, params) {
	var isValid = true;
	var otherFieldStr = params.get("otherField");
	var otherField = $(formName + "_" + otherFieldStr);
	if (field && otherField) {
		var otherFieldValueFilled = otherField.value && otherField.value.length > 0;
		var fieldValueFilled = field.value && field.value.length > 0;
		isValid = !(fieldValueFilled && otherFieldValueFilled);
	}
	return isValid;
}

/**
    * Check to see if at least one field has a value. 
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateAtLeastOneRequiredField(field, params) {
	var isValid = true;
	var otherFieldStr = params.get("otherField");
	var otherField = $(formName + "_" + otherFieldStr);
	if (field && otherField) {
		var otherFieldValueFilled = otherField.value && otherField.value.length > 0;
		var fieldValueFilled = field.value && field.value.length > 0;
		isValid = fieldValueFilled || otherFieldValueFilled;
	}
	return isValid;
}

/**
    * Check to see if a field startts with a specified string.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateStartsWithField(field, params) {
	var isValid = true;
	var startString = params.get("startValue");
	if (field && field.value && startString) {
		isValid = field.value.substring(0, startString.length) == startString;
	}
	return isValid;
}

/**
    * Check to see if a field is equal to another.
    * Fields are not checked if they are disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateEqualToField(field, params) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'select-one' || 
			field.type == 'textarea' || 
			field.type == 'password' || 
			field.type == 'file') && 
			(field.value.length > 0)) {

			var equalToStr = params.get("equalTo");
			var formName = field.form.name;
			var equalToElement = $(formName + "_" + equalToStr);
			isValid = false;
			if (equalToElement && field.value == equalToElement.value) {
				isValid = true;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if a field is not equal to another.
    * Fields are not checked if they are disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateNotEqualToField(field, params) {
	var isValid = true;
	if (field) {
		if ((field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'select-one' || 
			field.type == 'textarea' || 
			field.type == 'password' || 
			field.type == 'file') && 
			(field.value.length > 0)) {

			var equalToStr = params.get("notEqualTo");
			var formName = field.form.name;
			var equalToElement = $(formName + "_" + equalToStr);
			isValid = false;
			if (equalToElement && field.value != equalToElement.value) {
				isValid = true;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if a field is in a valid integer range.
    * Fields are not checked if they are disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateInRangeField(field, params) {
	var isValid = true;
	var value = '';
	if (field) {
		if (field.type == 'hidden' || 
			field.type == 'text' || 
			field.type == 'textarea' || 
			field.type == 'radio') {
			value = field.value;
		}
		if (field.type == 'select-one') {
			var si = field.selectedIndex;
			if (si >= 0) {
				value = field.options[si].value;
			}
		}
		if (value.length > 0) {
			var iMin = parseInt(params.get("inRangeMin"),10);
			var iMax = parseInt(params.get("inRangeMax"),10);
			var iValue = parseInt(value, 10);
			if (! (iValue >= iMin && iValue <= iMax)) {
				isValid = false;
			}
		}
	}
	return isValid;
}

/**
    * Check to see if a field value is numeric.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validateNumericField(field, params) {

	var mustBeInteger = params.get("mustBeInteger");
	if (mustBeInteger && mustBeInteger == "true") {
		return validateIntegerField(field, params);
	}
	return validateFloatField(field, params);
}

/**
    * Check to see if a field is a valid persolal id checknumber.
    * Field is not checked if it is disabled.
    * @param field The field validation is taking place on.
    * @param params A hash map with optional extra parameters.    
    */
function validatePersonalIdField(field, params) {
	var isValid = true;
	
	var country = params.get("Locale");

	var dateFormat = params.get("dateFormatStrict");
	if (!dateFormat) {
		dateFormat = params.get("dateFormat");
	}
	params.set("personalId","true");
	if (field) {
		if (country && country.indexOf("_SE") > -1 && field.value) {
			if (field.value.length == dateFormat.length && validateDateFormatField(field,params)) {
				isValid = jcv_luhnCheck(field.value);
			} else {
				isValid = false;
			}

		}
		if (country && country.indexOf("_NO") > -1 && field.value && validateDateFormatField(field,params)) {
			if (field.value.length == 11) {
				isValid = isChecksumCorrect_NO(field.value);
			} else {
				isValid = false;
			}
		}

		if (country && country.indexOf("_DK") > -1 && field.value && validateDateFormatField(field,params)) {
			if (field.value.length == 10) {
				isValid = true; //isChecksumCorrect_DK (field.value);
			} else {
				isValid = false;
			}
		}

	}
	return isValid;
}

function isValidDate(field,params) {
	var bValid = true;
	var value = field.value;
	var isStrict = true;
	var datePatterns = params.get("dateFormatStrict");
	// try loose pattern
	if (datePatterns == null) {
		datePatterns = params.get("dateFormat");
		isStrict = false;
	}

	if (field) {
		if ((field.type == 'text' || 
			field.type == 'textarea') && 
			(value.length >= 6 ) && (datePatterns.length > 0)) {


			var patternTokens = datePatterns.split(",");
			for (var i = 0; i < patternTokens.length; i++) {
				bValid = true;
				var datePattern = patternTokens[i];
				var MONTH = "MM";
				var DAY = "dd";
				var YEAR = "yyyy";
				var XXXX = "x";
				if (datePattern.indexOf(XXXX) > -1) {
					value = value.substring(0,6);
				}
				var orderMonth = datePattern.indexOf(MONTH);
				var orderDay = datePattern.indexOf(DAY);
				if (datePattern.indexOf(YEAR) == -1) {
					YEAR = "yy";
				}
				var orderYear = datePattern.indexOf(YEAR);
				if ((orderDay < orderYear && orderDay > orderMonth)) {
					var iDelim1 = orderMonth + MONTH.length;
					var iDelim2 = orderDay + DAY.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderDay && iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else if (iDelim1 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					} else if (iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (!jcv_isValidDate(matched[2], matched[1], matched[3])) {
							bValid = false;
						}
						break;
					} else {
						bValid = false;
					}
				} else if ((orderMonth < orderYear && orderMonth > orderDay)) {
					var iDelim1 = orderDay + DAY.length;
					var iDelim2 = orderMonth + MONTH.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderMonth && iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else if (iDelim1 == orderMonth) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})(\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					} else if (iDelim2 == orderYear) {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})(\\d{" + YEAR.length + "})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{" + YEAR.length + "})$") : new RegExp("^(\\d{1,2})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{" + YEAR.length + "})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (!jcv_isValidDate(matched[1], matched[2], matched[3])) {
							bValid = false;
						}
						break;
					} else {
						bValid = false;
					}
				} else if ((orderMonth > orderYear && orderMonth < orderDay)) {
					var iDelim1 = orderYear + YEAR.length;
					var iDelim2 = orderMonth + MONTH.length;
					var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
					var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
					if (iDelim1 == orderMonth && iDelim2 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})(\\d{2})(\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})(\\d{1,2})(\\d{1,2})$");
					} else if (iDelim1 == orderMonth) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})(\\d{2})[" + delim2 + "](\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})(\\d{1,2})[" + delim2 + "](\\d{1,2})$");
					} else if (iDelim2 == orderDay) {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{2})(\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{1,2})(\\d{1,2})$");
					} else {
						dateRegexp = isStrict ? new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{2})$") : new RegExp("^(\\d{" + YEAR.length + "})[" + delim1 + "](\\d{1,2})[" + delim2 + "](\\d{1,2})$");
					}
					var matched = dateRegexp.exec(value);
					if (matched != null) {
						if (!jcv_isValidDate(matched[3], matched[2], matched[1])) {
							bValid = false;
						}
						break;
					} else {
						bValid = false;
					}
				} else {
					bValid = false;
				}
			}
		} else if (field && field.type == 'select-one') {
			bValid = validateIsCompleteDateField(field, params);
		}
	}
	return bValid;

}

function validateOtherFieldsField (field,params) { 
	var isValid = true;
	var otherFieldToValidate = params.get("otherFieldNames");
	var otherFields = otherFieldToValidate.split(",");
	for (var i = 0; i < otherFields.length; i++) {
		var otherField= otherFields[i];
		var functionToRun = field.form.name + '_' + otherField + 'Validate("' + field.form.name + '_' + otherField + '");';
		var tmpValid = eval(functionToRun);
		if (isValid) {
			isValid = tmpValid;
		}
	}
	return isValid;
}
/**
     * Checks whether a given credit card number has a valid Luhn checksum.
     * This allows you to spot most randomly made-up or garbled credit card numbers immediately.
     * Reference: http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html
     */
function jcv_luhnCheck(cardNumber) {
	if(cardNumber.length == 12){
		cardNumber = cardNumber.substring(2);
	}
	if (jcv_isLuhnNum(cardNumber)) {
		var no_digit = cardNumber.length;
		var oddoeven = no_digit & 1;
		var sum = 0;
		for (var count = 0; count < no_digit; count++) {
			var digit = parseInt(cardNumber.charAt(count),10);
			if (! ((count & 1) ^ oddoeven)) {
				digit *= 2;
				if (digit > 9) digit -= 9;
			};
			sum += digit;
		};
		if (sum == 0) return false;
		if (sum % 10 == 0) return true;
	};
	return false;
}

function jcv_isLuhnNum(argvalue) {
	argvalue = argvalue.toString();
	if (argvalue.length == 0) {
		return false;
	}
	for (var n = 0; n < argvalue.length; n++) {
		if ((argvalue.substring(n, n + 1) < "0") || (argvalue.substring(n, n + 1) > "9")) {
			return false;
		}
	}
	return true;
}

/**
    * Check to see if combination of day, month, year is valid
    * @param day.
    * @param month.
    * @param year.        
    */
function jcv_isValidDate(inDay, inMonth, inYear) {

	var day = parseInt(inDay,10); 
	var month = parseInt(inMonth,10);
	var year = convertToFourDigitYear(inYear);

	if (month < 1 || month > 12) {
		return false;
	}
	if (day < 1 || day > 31) {
		return false;
	}
	if ((month == 4 || month == 6 || month == 9 || month == 11) && (day == 31)) {
		return false;
	}
	if (month == 2) {
		var leap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
		if (day > 29 || (day == 29 && !leap)) {
			return false;
		}
	}
	return true;
}

function convertToFourDigitYear(inYear) {
	var year = parseInt(inYear,10);
	if (inYear != null && inYear.length == 2) {
		var nowFullYear = new String(new Date().getFullYear());
		var nowIntShortYear = parseInt(nowFullYear.substring(2),10);
		if (year < nowIntShortYear) {
			var nowCentury = nowFullYear.substring(0,2);
			year = (parseInt(nowCentury,10) * 100) + year;
		} else {
			var nowCentury = nowFullYear.substring(0,2);
			year = ((parseInt(nowCentury,10)-1) * 100) + year;
		}
		return year
	}
	return year;
}
/**
     * Reference: Sandeep V. Tamhankar (stamhankar@hotmail.com),
     * http://javascript.internet.com
     */
function jcv_checkEmail(emailStr) {
	if (emailStr.length == 0) {
		return true;
	}
	var emailPat = "^[^@, ]+@[^@]+\\.[^@\\.]{2,}$";
	if (!emailStr.match(emailPat)) {
		//     	if (!jcv_matchPattern(emailStr, emailPat)) {
		return false;
	}
	return true;
}

// Trim whitespace from left and right sides of s.
function trim(s) {
	return s.replace(/^\s*/, "").replace(/\s*$/, "");
}

/**
   * Check a value only contains valid numeric digits
   * @param argvalue The value to check.
   */
function jcv_isAllDigits(argvalue) {
	argvalue = argvalue.toString();
	var validChars = "0123456789";
	var startFrom = 0;
	if (argvalue.substring(0, 2) == "0x") {
		validChars = "0123456789abcdefABCDEF";
		startFrom = 2;
	} else if (argvalue.charAt(0) == "0") {
		validChars = "01234567";
		startFrom = 1;
	} else if (argvalue.charAt(0) == "-") {
		startFrom = 1;
	}

	for (var n = startFrom; n < argvalue.length; n++) {
		if (validChars.indexOf(argvalue.substring(n, n + 1)) == -1) return false;
	}
	return true;
}

/**
   * Check a value only contains valid decimal digits
   * @param argvalue The value to check.
   */
function jcv_isDecimalDigits(argvalue) {
	argvalue = argvalue.toString();
	var validChars = "0123456789";

	var startFrom = 0;
	if (argvalue.charAt(0) == "-") {
		startFrom = 1;
	}

	for (var n = startFrom; n < argvalue.length; n++) {
		if (validChars.indexOf(argvalue.substring(n, n + 1)) == -1) return false;
	}
	return true;
}

function jcv_matchPattern(value, mask) {
	return mask.exec(value);
}

/* Helper for date calculations */
function isLeapYear(year) {
	if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
		return true;
	}
	return false;
}

/** Helper function for calculating checksum for NO */
function isChecksumCorrect_NO(pid) {
	if (!pid || pid.length != 11) {
		return false;
	}

	var v1 = [3, 7, 6, 1, 8, 9, 4, 5, 2];
	var v2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];

	var checksum = parseInt(pid.substring(9, 10),10);
	var sum = 0;
	var digit = 0;

	for (var i = 0; i < 9; i++) {
		digit = parseInt(pid.substring(i, i + 1),10);
		sum += digit * v1[i];
	}

	if ((sum % 11) != (11 - (checksum == 0 ? 11 : checksum))) {
		return false;
	}

	var checksum = parseInt(pid.substring(10, 11),10);
	var sum = 0;
	var digit = 0;

	for (var i = 0; i < 10; i++) {
		digit = parseInt(pid.substring(i, i + 1),10);
		sum += digit * v2[i];
	}

	if ((sum % 11) != (11 - (checksum == 0 ? 11 : checksum))) {
		return false;
	}
	return true;
}

/** Helper function for calculating checksum for DK ( probalbly obsolete, currently not used) */
function isChecksumCorrect_DK(pid) {
	if (!pid || pid.length != 10) {
		return false;
	}

	var v1 = [4, 3, 2, 7, 6, 5, 4, 3, 2, 1];

	var checksum = parseInt(pid.substring(9, 10),10);
	var sum = 0;
	var digit = 0;

	for (var i = 0; i < 9; i++) {
		digit = parseInt(pid.substring(i, i + 1),10);
		sum += digit * v1[i];
	}

	if ((sum % 11) != (11 - (checksum == 0 ? 11 : checksum))) {
		return false;
	}

	return true;
}
