/* CopyrightTag: Copyright (c) 2000-2008 Satmetrix Systems, Inc. All Rights Reserved. */
/* $Id: validate.js,v 1.22 2008/01/08 23:55:21 build Exp $ */
/*
 * Javascript functions for validation of form fields in efocus.
 *
 * History:
 *   Feb 15 2001   jwalton - because Integer type questions cannot have empty
 *                           values, use "0" to mean no selection for Select widgets
 *                           (single selects only)
 *   Nov 07 2000   jwalton - created
 *
 */

/* Strings for use in error messages.  These and the text in the "newError" function may be altered as desired. */
/* The strings were moved to the whtml files for i18n purposes. */

// DON'T CHANGE THESE!  They have to match values passed in from the server.
var INT_TYPE = "Int";
var DOUBLE_TYPE = "Double";
var STRING_TYPE = "String";
var DATE_TYPE = "Date";

// This note is tacked on to the beginning of the error message before shown to the user.  Change text as desired.
var errorNote = "";

var errors = "";

// Page level validation check
function checkValidateFields() {
	var errorMessage = "";
	
	//validate all expression based field validators
	var toValidate = expressionFieldsToValidate.keys();
	for (var prop in toValidate) {
		
		var value = toValidate[prop].element.value;
		var expression = toValidate[prop].expression;
		var displayFieldName = toValidate[prop].displayFieldName;
		var conceptDisplayName = toValidate[prop].conceptDisplayName;
		
		// only validate if the value is actually populated
		if (value != null && value != "") {
			if (expression.test(value) == false) {
				errorMessage += "    " + displayFieldName;
				if (conceptDisplayName != null && conceptDisplayName != '') {
					errorMessage += "/" + conceptDisplayName;
				}
				errorMessage += "\n";
			}
		}
	}
	
	// validate all number range validators
	toValidate = numberRangeFieldsToValidate.keys();
	for (var prop in toValidate) {
		var value = toValidate[prop].element.value;
		var min = toValidate[prop].min;
		var max = toValidate[prop].max;
		var displayFieldName = toValidate[prop].displayFieldName;
		var conceptDisplayName = toValidate[prop].conceptDisplayName;
		
		// only validate if the value is actually populated
		if (value != null && value != "") {
			if (isValidNumberAndInRange(value, min, max) == false) {
				errorMessage += "    " + displayFieldName;
				if (conceptDisplayName != null && conceptDisplayName != '') {
					errorMessage += "/" + conceptDisplayName;
				}
				errorMessage += "\n";
			}
		}
	}

	// validate all date validators
	toValidate = dateFieldsToValidate.keys();
	for (var prop in toValidate) {
		var value = toValidate[prop].element.value;
		var format = toValidate[prop].format;
		var displayFieldName = toValidate[prop].displayFieldName;
		var conceptDisplayName = toValidate[prop].conceptDisplayName;
		
		// only validate if the value is actually populated
		if (value != null && value != "") {
			if (isValidDate(value, format) == false) {
				errorMessage += "    " + displayFieldName;
				if (conceptDisplayName != null && conceptDisplayName != '') {
					errorMessage += "/" + conceptDisplayName;
				}
				errorMessage += "\n";
			}
		}
	}
	
	// validate all currency range validators
	toValidate = currencyRangeFieldsToValidate.keys();
	for (var prop in toValidate) {
		var value = toValidate[prop].element.value;
		var min = toValidate[prop].min;
		var max = toValidate[prop].max;
		var displayFieldName = toValidate[prop].displayFieldName;
		var conceptDisplayName = toValidate[prop].conceptDisplayName;
		
		// only validate if the value is actually populated
		if (value != null && value != "") {
			if (isValidCurrencyInRange(value, min, max) == false) {
				errorMessage += "    " + displayFieldName;
				if (conceptDisplayName != null && conceptDisplayName != '') {
					errorMessage += "/" + conceptDisplayName;
				}
				errorMessage += "\n";
			}
		}
	}
	
	if (errorMessage != "") {
		var alertMessage = stringReplace1(INVALID_FIELD_STRING, errorMessage, false);
		alert(alertMessage);
		return false;
	} else {
		return true;
	}
}

/*
 * Validates that one of the radio buttons has been selected.  
 * Returns an error message if not.
 */
function validateRadio (formName, radioName, radioDescr) {

	var radioObj = eval ("document." + formName + "." + radioName);
	if (radioObj.length) {
		for (var i=0; i<radioObj.length; i++) {
			if (radioObj[i].checked) {
				return "";
			}
		}
	} else if (radioObj.checked) { // is possible radio group has only 1 button, then is not an array
		return "";
	}
	return newError(MANDATORY_ERROR_STRING, radioName, radioDescr);
}


/*
 * Validated that at least one checkbox with the given name has been checked.  
 * Returns an error message if not.
 */
function validateCheckbox (formName, checkName, checkDescr) {
	var formObj	= eval("document." + formName);
	for (var i=0; i<formObj.elements.length; i++) {
		if (formObj.elements[i].name == checkName && formObj.elements[i].checked) {
			return "";
		}	
	}
	return newError(MANDATORY_ERROR_STRING, checkName, checkDescr);
}

/*
 * Validates that the selected item in the selection list has a non-emtpy value.  
 * Returns an error message if not.
 */
function validateSelect (formName, selName, selDescr, isMulti) {
	var selObj = eval ("document." + formName + "." + selName);
	var selIndex = selObj.selectedIndex;
	if (selIndex < 0) {
		return newError(MANDATORY_ERROR_STRING, selName, selDescr);
	} 
	var val = selObj.options[selIndex].value;
	if (isEmpty(val)) {
		return newError(MANDATORY_ERROR_STRING, selName, selDescr);
	}
	return "";
}

/*
 * Validates that the contents of the given text field are not empty.  
 * Also checks the type of the content if not empty.
 * Returns an error message if empty.
 */
function validateTextfield (formName, textName, textDescr, type, maxLength) {
	
        var textObj = eval("document." + formName + "." + textName);
	var content = textObj.value;
        if (isEmpty(content)) {
                return newError(MANDATORY_ERROR_STRING, textName, textDescr);
	} 
	return validateTextType(formName, textName, textDescr, type, maxLength);
}

/*
 * Validates that the contents of the given text field are not empty only if
 * the given that the multiple answer type is selected with the flagged value.
 * Also checks the type of the content if not empty.
 * Returns an error message if problems found.
 */
function validateTextfieldBasedOn (formName, textName, textDescr, type, dependentFieldName, 
      dependentDescription, dependentFieldValue, dependentType, maxLength) {
    if (isBasedOnSelected( formName, dependentFieldName, dependentFieldValue, dependentType )){
        //then this is required, thus the validation
        return validateTextfield (formName, textName, textDescr, type, maxLength);
    }
	 else { // should be empty
		var textObj = eval("document." + formName + "." + textName);
		var content = trim(textObj.value);

		if (content == null || content == "") {
			return "";
		}
		else {
			 var errorStr = stringReplace2(OTHER_CHOICE_ERROR_STRING, 
			     ( dependentDescription == null ? dependentFieldValue : dependentDescription ), 
				  ( textDescr == null ? textName : textDescr ), 
				  false) + "\r\n\r\n";

			 return errorStr;
		}
	}
}

function isBasedOnSelected( formName, dependentFieldName, dependentFieldValue, dependentType ){
    
    if (dependentType == "Select"){
        var selObj = eval ("document." + formName + "." + dependentFieldName);
	
        for (var i=0; i < selObj.options.length; i++){  
            if (selObj.options[i].selected){
                if (selObj.options[i].value == dependentFieldValue){
                    return true;
                }
            }
        }
       
		return false;
    }
    else if (dependentType == "CheckBox"){
        var formObj	= eval("document." + formName);
		for (var i=0; i<formObj.elements.length; i++) {
            if (formObj.elements[i].name == dependentFieldName){
                if (formObj.elements[i].checked) {
                    if (formObj.elements[i].value == dependentFieldValue) {
                        return true;
                    }
                }
            }	
		}
        return false;
    }
    else if (dependentType == "RadioButton"){
        var radioObj = eval ("document." + formName + "." + dependentFieldName);
		for (var i=0; i<radioObj.length; i++) {
            if (radioObj[i].checked) {
                if (radioObj[i].value == dependentFieldValue){
                    return true;
                }
            }
		}
        return false;
    }

    return false;
}

/*
 * Validate that the contents of the text field are of the given type.
 */
function validateTextType(formName, textName, textDescr, type, maxLength) {
	var textObj = eval("document." + formName + "." + textName);
	// Replace any numbers with high order bytes as FF.
 	var content = replaceHighOrderNumbers(trim(textObj.value));
	if (content == null || content == "") {
		return "";
	}

	if (content.length > maxLength) {
	   return newError(LENGTH_ERROR_STRING, textName, textDescr);
	}

	if (type == "Int") {
		if (! isJavaInteger(trim(content))) {
			return newError(INTEGER_ERROR_STRING, textName, textDescr);
		}
	} else if (type == "Double") {
		if (! isNumber(trim(content))) {
			return newError(DOUBLE_ERROR_STRING, textName, textDescr);
		}
	}
	return "";
}

function isJavaInteger(string) {
   if (!isInteger(string)) {
	   return false;
   }

	var positive = Number(string);

	if (positive < 0) {
	  positive = -positive;
	}
	
	return positive <= 0x7fffffff;
}

/*
 * Creates an error string by appending the error to the description or name of the field.
 */
function newError(newError, name, descr) {
    errorStr = stringReplace1(newError, (descr == null ? name : descr), false) + "\r\n\r\n";
    return errorStr;
}


//
// Currency Range Validation Support
//

function removeCommasFromString(string){
	while (string.search(/,/) > -1){
		string = string.replace(/,/, "");
	}
	return string;
}

function isValidCurrencyInRange(fieldValue, min, max){

	var re = /^-?((\d*(\.\d{0,2})?)|(\d{1,3}(,\d{3})+(\.\d{0,2})?))$/;

	if (re.test(fieldValue)){
		
		//filter out any commas if present
		var cleanValue = removeCommasFromString(fieldValue);
		
		if (max != null){
			if (cleanValue > max){
				return false;
			}	
		}
		
		if (min != null){
			if (cleanValue < min){
				return false;
			}
		}
	
		return true;	
	}
	return false;
}

function validateCurrencyRangeField(fieldValue, min, max, errorMessage){
	
	//only validate if field is something
	if (fieldValue != null && fieldValue != ""){
		
		if (isValidCurrencyInRange(fieldValue, min, max)){
			return true;
		} else {
			alert(errorMessage);
			return false;
		}		
	}
}

var currencyRangeFieldsToValidate = new Dictionary();

//CurrencyRangeValidate 'object'
function CurrencyRangeValidateElement(element, min, max, displayFieldName, conceptDisplayName){
	this.element = element;
	this.min = min;
	this.max = max;
	this.displayFieldName = displayFieldName;
        this.conceptDisplayName = conceptDisplayName;
}

function addBatchCurrencyRangeValidator(element, min, max, displayFieldName, conceptDisplayName){
	
	//create new entry for validation
	var newElement = new CurrencyRangeValidateElement(element, min, max, displayFieldName, conceptDisplayName);
	
	//add element to the dictionary
        var key = element.name;
        if (conceptDisplayName != null && conceptDisplayName != ''){
            key += concept_separator;
            key += conceptDisplayName;
        }
	currencyRangeFieldsToValidate.setAt(key, newElement);
}





//
// Number Range Validation Support
//

function isValidNumberAndInRange(fieldValue, min, max){

	var re = /^-?\d+$/;
	if (re.test(fieldValue)){
		if (max != null){
			if (fieldValue > max){
				return false;
			}
		}
		
		if (min != null){
			if (fieldValue < min){
				return false;
			}
		}
		
		return true;
	}
	return false;
}

function validateNumberRangeField(field, min, max, errorMessage, defaultValue){
	            
	//only validate if field is something
	// Also replace any numbers with high order bytes as FF.
	var fieldValue = replaceHighOrderNumbers(field.value);
	if (fieldValue != null) {
		
		if (isValidNumberAndInRange(fieldValue, min, max)){
			return true;
		}		
	}
	resetTextFieldOnError(errorMessage, field, fieldValue, defaultValue);
	return false;
}

// Needed because JSP mechanism requires different argument order
function jspValidateNumberRangeField(min, max, field, errorMessage, defaultValue) {
   return validateNumberRangeField(field, min, max, errorMessage, defaultValue);
}

var numberRangeFieldsToValidate = new Dictionary();

//numberRangeValidate 'object'
function NumberRangeValidateElement(element, min, max, displayFieldName, conceptDisplayName){
	this.element = element;
	this.min = min;
	this.max = max;
	this.displayFieldName = displayFieldName;
        this.conceptDisplayName = conceptDisplayName;
}

function addBatchNumberRangeValidator(element, min, max, displayFieldName, conceptDisplayName){

    //create new entry for validation
    var newElement = new NumberRangeValidateElement(element, min, max, displayFieldName, conceptDisplayName);
	
    //add element to the dictionary
    var key = element.name;
    if (conceptDisplayName != null && conceptDisplayName != ''){
        key += concept_separator;
        key += conceptDisplayName;
    }
    numberRangeFieldsToValidate.setAt(key, newElement);
}

// Needed because JSP mechanism requires different argument order
function addJspBatchNumberRangeValidator(min, max, element, displayFieldName, conceptDisplayName) {
   return addBatchNumberRangeValidator(element, min, max, displayFieldName, conceptDisplayName);
}


//
// Date Validation Support
//

function isValidDate(value, format) {
   var monthFirst = true;
	var re;

   // re doesn't have anchor at end, because Java DateFormat ignores
	// stuff at end.
	if (format == "mm-dd-yyyy") {
	   re = /^(\d{2})[-\.\\\/](\d{2})[-\.\\\/](\d{4})/;
   }
	else if (format == "mm-dd-yy") {
      re = /^(\d{2})[-\.\\\/](\d{2})[-\.\\\/](\d{2})/;
   }
	else if (format == "dd-mm-yyyy") {
	   re = /^(\d{2})[-\.\\\/](\d{2})[-\.\\\/](\d{4})/;
		monthFirst = false;
   }
	else { // don't know how to format
	   return false;
   }

	var match = re.exec(value);

	if (!match || match.length < 4) {
	   return false;
   }

   var year = match[3];

	if (year < 100) { 
	   // adjust to current century.  This is consistent with SimpleDateFormat -
		// 20 years before now, or 80 years after.
	   var now = new Date();
		var thisYear = now.getFullYear();

		year = Number(year) + thisYear - (thisYear % 100);

		if (year > thisYear + 80) {
		   year -= 100;
		}
		else if (year < thisYear - 20) {
		   year += 100;
		}
	}
	
	var day   = monthFirst ? match[2] : match[1];
	var month = monthFirst ? match[1] : match[2];

   // alert("Date: " + month + "/" + day + "/" + year);
	if (day < 1 || day > 31 || month < 1 || month > 12) {
	   return false;
	}

   // check day by setting date, and making sure month doesn't change
	var date = new Date(year, month - 1, day);
   var calcMonth = date.getMonth();
	
   // if they are equal, day was too high
   return calcMonth < month;
}

function validateDateField(field, format, errorMessage, defaultValue){
	            
	//only validate if field is something
	// Also replace any numbers with high order bytes as FF.
	var fieldValue = replaceHighOrderNumbers(field.value);
	if (fieldValue != null) {
		
		if (isValidDate(fieldValue, format)){
			return true;
		}		
	}
	resetTextFieldOnError(errorMessage, field, fieldValue, defaultValue);
	return false;
}

// Needed because JSP mechanism requires different argument order
function jspValidateDateField(format, field, errorMessage, defaultValue) {
   return validateDateField(field, format, errorMessage, defaultValue);
}

var dateFieldsToValidate = new Dictionary();

// dateValidate 'object'
function DateValidateElement(element, format, displayFieldName, conceptDisplayName){
	this.element = element;
	this.format = format;
	this.displayFieldName = displayFieldName;
	this.conceptDisplayName = conceptDisplayName;
}

function addBatchDateValidator(element, format, displayFieldName, conceptDisplayName){

    //create new entry for validation
    var newElement = new DateValidateElement(element, format, displayFieldName, conceptDisplayName);
	
    //add element to the dictionary
    var key = element.name;
    if (conceptDisplayName != null && conceptDisplayName != ''){
        key += concept_separator;
        key += conceptDisplayName;
    }
    dateFieldsToValidate.setAt(key, newElement);
}

// Needed because JSP mechanism requires different argument order
function addJspBatchDateValidator(format, element, displayFieldName, conceptDisplayName) {
   return addBatchDateValidator(element, format, displayFieldName, conceptDisplayName);
}

//
// Expression Validation Support
//
function validateExpressionField(field, expression, errorMessage, defaultValue){

	var fieldValue = replaceHighOrderNumbers(field.value);
	if (fieldValue != null) {
		if (expression.test(fieldValue)){
			return true;
		}
	}
	resetTextFieldOnError(errorMessage, field, fieldValue, defaultValue);
	return false;
} 

var expressionFieldsToValidate = new Dictionary();

var concept_separator = "___CONCEPT_SEPARATOR___";

//expressionValidateElement 'object'
function ExpressionValidateElement(element, expression, displayFieldName, conceptDisplayName){
	this.element = element;
	this.expression = expression;
	this.displayFieldName = displayFieldName;
  this.conceptDisplayName = conceptDisplayName;
}

function addBatchExpressionValidator(element, expression, displayFieldName, conceptDisplayName){

	//create a new entry for validaiton
	var newElement = new ExpressionValidateElement(element, expression, displayFieldName, conceptDisplayName);
		
	//add the element to the dictionary
        var key = element.name;
        if (conceptDisplayName != null && conceptDisplayName != ''){
            key += concept_separator;
            key += conceptDisplayName;
        }
	expressionFieldsToValidate.setAt(key, newElement);
}




/*
 * Method that handles mutual exclusive choices from a set.
 */
function checkMutEx(choiceIsMutEx, elementsToCheck, widget, mutExElements){

    	if (choiceIsMutEx){
    	
    		//this widget is flagged as being mutually exclusive	
    		
    		if (widget.checked){
    		
    			//loop through all other elements and if any are checked fail
    			for (i=0; elementsToCheck != null && i < elementsToCheck.length; i++){

    				if (elementsToCheck[i].checked && elementsToCheck[i] != widget){
    					alert(THIS_MUTU_EXC_ERROR_STRING);
						widget.checked = false;
    					return false;
    				}
    			}
    		}
    			
		} else {			
	
			//this widget it NOT flagged as being mutually exclusive
			
			if (widget.checked){
			
				//since it is checked we need to make sure none of the mutual exclusive checkboxes are checked.
			
    			//loop through all others and if one of the mutExElements are seleceted fail
    		
				for (x=0; elementsToCheck != null && x < elementsToCheck.length; x++){
				
					var tempCheckbox = elementsToCheck[x];
				
					//if the one being iterated through matches the one that called this method then skip it
					if (tempCheckbox == widget){
						continue;
					}
				
					if (tempCheckbox.checked){
					
						var tempCheckboxName = tempCheckbox.value;
						
						for (n=0; mutExElements != null && n < mutExElements.length; n++){
							if (tempCheckboxName == mutExElements[n]){
								alert(OTHER_MUTU_EXC_ERROR_STRING);
								widget.checked = false;
								return false;
							}
						}
					}
				}
			} else {
				//ignore it if the widget is not being checked, which just means that the user unchecked the box
			}
    	}
    	return true;    
    }



    //
    // Mutual Exclusive MulitSelect support
    //

   var lastGoodStatesForMultiSelects = new Dictionary();
    
    function checkMultiSelectMutEx(multiSelect, mutExIndexesArray){
       	
    	//first make sure that something is actually selcted
    	if (multiSelect == null || multiSelect.selectedIndex < 0){
    		return;
    	}
    	
    	var multiSelectSelected = false;
    	var nonMultiSelectSelected = false;
    	
    	//
    	//validate
    	//
    	
    	//iterate through all options
    	for (var i = 0; i < multiSelect.length; i++){
     		
     		if (multiSelect.options[i].selected){		

				//check to see if this is a mutex choice
				if (isObjectInArray(i, mutExIndexesArray)){
					
					//reject if another mutext already selected
					if (multiSelectSelected){
						alert(OTHER_MUTU_EXC_ERROR_STRING);
						revertBack(multiSelect, lastGoodStatesForMultiSelects.lookUp(multiSelect.name));
						return;
					}
					
					//reject if any non mutex are selected
					if (nonMultiSelectSelected){
						alert(THIS_MUTU_EXC_ERROR_STRING);
						revertBack(multiSelect, lastGoodStatesForMultiSelects.lookUp(multiSelect.name));
						return;
					}
					
					multiSelectSelected = true;
				} else {
					//not mutex
					
					//reject if mutex already selected				
					if (multiSelectSelected){
						alert(OTHER_MUTU_EXC_ERROR_STRING);
						revertBack(multiSelect, lastGoodStatesForMultiSelects.lookUp(multiSelect.name));
						return;
					}
					
					nonMultiSelectSelected = true;
				}
    		}
    	}
    	
      	//
    	//save 'last-good' state
    	//
    	var selectedIndexes = new Dictionary();
    	for (var i = 0; i < multiSelect.length; i++){
     		if (multiSelect.options[i].selected){		
    			selectedIndexes.setAt(i, i);
      		}
    	}	
    	lastGoodStatesForMultiSelects.setAt(multiSelect.name, selectedIndexes);
    }
    
    
    
    function revertBack(multiSelectElement, selectedChoicesDictionary){
    	
    	//if we don't have any selected choices, that means that none are saved
    	if (selectedChoicesDictionary == null){
      		for (var i = 0; i < multiSelectElement.options.length; i++){
    			multiSelectElement.options[i].selected = false;
			}
    		return;
    	}
		
    	//loop thorugh all items and set selected if necessary...
    	for (var i = 0; i < multiSelectElement.options.length; i++){
    		
    		if (selectedChoicesDictionary.lookUp(i) != null ){
    			multiSelectElement.options[i].selected = true;
    		} else {
    			multiSelectElement.options[i].selected = false;
    		}
    	}
    	
    }
	
	function isObjectInArray(object, array){
		
		for (var i = 0; i < array.length; i++){
			if (array[i] == object){
				return true;
			}
		}
		return false;
	}
	
function resetTextFieldOnError(errorMessage, textField, fieldValue, defaultValue) {
	if (fieldValue == defaultValue) {
	   return; // avoid some loops this way - catch error when press next.
	}

	if ( isIEBrowser() ) {
		alert(errorMessage);
		textField.select();
		textField.focus();
		textField.hideFocus = false;
	}
	else {
		textField.value = defaultValue;
		alert(errorMessage);
	}
}

/**
 *  Returns true if the browser is Netscape 
 */
function isNetscapeBrowser() {
	var appName = navigator.appName;
	return ( appName && appName.toLowerCase() == "netscape" );
}

/**
 *  Returns true if the browser is Internet Explorer
 */
function isIEBrowser() {
	var appVer = navigator.appVersion
	return ( appVer && appVer.toLowerCase().indexOf('msie') > -1 );
}

    
