//***************************************************************
// Validation Functions for ZPortal                        
//                                                              
// Modification History                                         
//                                                              
// 02/01/02 AC  Documented and fixed a few bugs
//                                                              
//***************************************************************

// for text fields:
//  if mandatory, then text must be != null or ""
//  if range is given, must be > min and < max
//  if type != "any", validation on char set is performed

// for checkbox groups
//  if select = any, must be at least one checkbox selected
//  

/* -----------------------------------------------------------------------------------------
// Sets up the meta-data to be validated
//
//   Parameters:
//   form_name   The form to be validated
//   Returns     Stringarray containing the meta-data
*/

/*****************************************************************
// The following function should be implemented in your stylesheet
// It is included here with examples for your reference.
*****************************************************************/
/*
   function field_metadata(form_name)
   {
        var meta_data;

        // do the add_field calls for all fields requiring validation, e.g.        
        // see definition of add_field for description of parameters        
        
        add_field(meta_data, "search_term", form_name, item_name, true, "any", "0", "30");
        
        return meta_data;
   }    
*/

// change this to true to show debugging messages
var DEBUG=false;

/* -----------------------------------------------------------------------------------------
// Validate a form
//   This function should be called from your process_form function
//   Note: field_metadata MUST be called before this function
//
//   Parameters:
//   form_name   The form to validate
//   Returns     boolean Whether the form is valid or not.
*/

function validate_form(the_form)
{   
    var form_name = the_form.name;
    
    if (DEBUG)
        alert("in validate_form for " + form_name);

    var valid = true;
    var errMsg = "Validation has failed for the following reason(s):\n";

    // check if they have implemented the field_metadata function
    var meta_data = self.field_metadata;
    if(meta_data != null)
    {
        // go and get the metadata then
        var fields = field_metadata(form_name);

        if (DEBUG)
            alert("there are " + fields.length + " elements in fields");
    
        // loop through all the fields, calling validate_item on each one
        for(var loop = 0; loop < fields.length; loop=loop+8)
        {
            var pos = loop;
    
            var form      = fields[pos];
            var alias     = fields[++pos];
            var name      = fields[++pos];
            if (DEBUG)
                alert("processing field: " + field);
            
            var mandatory = fields[++pos];
            var type      = fields[++pos];
            var subtype   = fields[++pos];
            var min_size  = fields[++pos];
            var max_size  = fields[++pos];
            
            if (DEBUG)
                alert("check before validate_item: " + form + ", " + alias + ", " + name + ", " + mandatory + ", " + type + "." + subtype + ", " + min_size + ", " + max_size);
            
            var errText = validate_item(the_form, alias, name, mandatory, type, subtype, min_size, max_size);
            if(errText != "")
            {
                valid = false;
                errMsg += errText;
            }       
        }
    }
    else
    {
        if (DEBUG)
            alert("field_metadata implementation is compulsory");
    }
    
    if(valid == false)
        alert(errMsg);
    
//    if (DEBUG)    
//          alert("returning " + valid + " for form " + form_name);
    return valid;
}

/* -----------------------------------------------------------------------------------------
// Gets the text held by a control
//
//   Parameters:
//   form   The form that the control is contained on
//   name   The name of the control to get the text of
//   Returns text   The text of the control
*/

function get_text(form, name)
{
	if(name || name != "")
	{	
		var current_form = document[form];
		if(current_form)
		{
			var text_item = current_form[name];
			if (navigator.appName == 'Netscape' && navigator.appVersion.indexOf('4.') != -1)
			{
				if (text_item)
				{
                	if (text_item.type == 'select-one')
					{
                    	// item IS a select box 
						return text_item.options[text_item.selectedIndex].value;
					}
					else
					{
                    
						return text_item.value;
					}
				}
				else
					return null;
			}
			else
			{
				if (text_item)
					return text_item.value;
				else
					return null;
			}
		}		
	}
	return null;
}

/* -----------------------------------------------------------------------------------------
// Sets the text for a control
//
//   Parameters:
//   form   Form that the control is contained on
//   name   Name of the control to get the text of
//   value  The string to set
//   Returns null
*/

function set_text(form, name, value)
{
	if(name || name != "")
	{	
		var current_form = document[form];
		if(current_form)
		{
			var text_item = current_form[name];
			if (navigator.appName == 'Netscape' && navigator.appVersion.indexOf('4.') != -1)
			{
				text_item.value = value;
			}
			else
			{
				if (text_item)
					text_item.value = value;
			}
		}		
	}
	return null;
}

//-----------------------------------------------------------------------------------------

/* -----------------------------------------------------------------------------------------
// Count the number of characters in a string
//
//   Parameters:
//   text   String to count chars of
//   value  The character set to use in the count
//   Returns int  The number of characters in text
*/

function count_chars(text, char_set)
{	
	var count = 0;
	for (var pos = 0; pos < text.length; pos++)  
	{
		temp_char = text.charAt(pos);
		if (char_set.indexOf(temp_char, 0) != -1)
			count++;
	}
	return count;
}

/* -----------------------------------------------------------------------------------------
// Sets the text for a control
//
//   Parameters:
//   form   String to validate
//   name   String containing the acceptable characters
//   Returns boolean  Whether the text contains only characters from char_set
*/

function validate_chars(text, char_set)
{
	for (var pos = 0; pos < text.length; pos++)  
	{
		temp_char = text.charAt(pos);
		if (char_set.indexOf(temp_char, 0) == -1)
			return false;
	}
	return true;
}

/* -----------------------------------------------------------------------------------------
// Add a field to the meta_data array
//
//   Parameters:
//   meta_data   Stringarray to add the item to
//   form        The form that the control is contained on
//   alias       Descriptive String to be used in error messages
//   item_name   String name of the control to validate
//   mandatory   boolean Whether the field is mandatory 
//   type        String main type of control (e.g. text, checkbox)
//   type        String sub-type of control (see validate_type for list of permissible types)
//   min_size    int minimum length of data
//   max_size    int maximum length of data
//   Returns     Stringarray containing the new item
*/

function add_field(meta_data, form, alias, item_name, mandatory, type, subtype, min_size, max_size)
{
    var pos = 0;

    if (meta_data != null)
        pos = meta_data.length;        

    if (DEBUG)
        alert("meta_data length=" + pos);

    meta_data[pos] = form; pos++;
    meta_data[pos] = alias; pos++;
    meta_data[pos] = item_name; pos++;
    meta_data[pos] = mandatory; pos++;
    meta_data[pos] = type; pos++;
    meta_data[pos] = subtype; pos++;
    meta_data[pos] = min_size; pos++;
    meta_data[pos] = max_size;

    if (DEBUG)
        alert("field added: " + form + "." + field_name);

    return meta_data;
}

/* -----------------------------------------------------------------------------------------
// Validates a form item
//
//   Parameters:
//   form   The form that the control is contained on
//   name   The name of the control to get the text of
//   Returns Boolean Whether validation was successful
*/

//function validate_item(the_form, name)
function validate_item(the_form, alias, name, mandatory, type, subtype, min_size, max_size)
{
    if (DEBUG)
        alert("in validate_item with values name=" + name); 
    
    var form = the_form.name;
	var result = true;

    var errText = "";
    if (type == "text")
    {
//        if (DEBUG)
//            alert("check before validate_text: " + form + ", " + alias + ", " + name + ", " + mandatory + ", " + type + "." + subtype + ", " + min_size + ", " + max_size);
        errText = validate_text(form, alias, name, mandatory, subtype, min_size, max_size);
    }
    else if(type == "checkbox")
    {
//        if (DEBUG)
//            alert("check before validate_checkbox: " + form + ", " + alias + ", " + name + ", " + subtype);
        errText = validate_checkbox(the_form, alias, name, subtype);
    }

    if(errText != "")
    {
        result = false;
    }			

    return errText;
}

/* -----------------------------------------------------------------------------------------
// Validate a text field
//
//   Parameters:
//   form_name   The form that the control is contained on
//   alias       Descriptive string to use in an error message
//   name        The name of the control to validate
//   mandatory   boolean Whether this is a mandatory field or not 
//   type        String data type for this field
//   min_size    int the minimum permissible length for the string
//   max_size    int the maximum permissible length for the string
//   Returns     String containing error message (if any)
*/

function validate_text(form_name, alias, name, mandatory, type, min_size, max_size)
{
    var errText = "";
    var text = get_text(form_name, name);

    if (DEBUG)
        alert("in validate_text with values text='" + text + "', mandatory=" + mandatory); 

    if(text != null)
    {
        if(text.length < 1)
        {
            if(mandatory)
                errText += alias + " must be populated" + "\n";
        }
        else
        {
            errText += validate_type(alias, text, type);
            errText += validate_range(alias, text, type, min_size, max_size);           
        }       
    }
    else
    {
        // Null mandatory items are invalid - doh
        if(mandatory)
            errText += alias + " must be populated" + "\n";

    }
    return errText;
}

/* -----------------------------------------------------------------------------------------
// Validate a checkbox group
//
//   Parameters:
//   form_name   The form that the control is contained on
//   alias       Descriptive string to use in an error message
//   name        The name of the control to validate
//   type        String data type for this field
//   Returns     String containing error message (if any)
*/

function validate_checkbox(the_form, alias, name, type)
{
    var errText = "";
    var selected = false;
    if (DEBUG)
        alert("in validate_checkbox with values name='" + name + "', type=" + type); 


    if(!the_form || !the_form.elements || !the_form.elements[name])
        return false;
    if (the_form.elements[name].name) 
    {
        // As a single element, could be a checkbox, a select list or an option 
        if (the_form.elements[name].checked || 
            the_form.elements[name].selectedIndex > -1 || 
            the_form.elements[name].selected) 
            selected = true;
    }
    else if (the_form.elements[name].length > 0) 
    {
        // Each element could be a checkbox, a select list or an option
        for (i=0; the_form.elements[name].length > i && !selected ; i++) 
        {
            if (the_form.elements[name][i].checked || 
                the_form.elements[name][i].selectedIndex > -1 || 
                the_form.elements[name][i].selected) 
            {
                selected = true;
            }
        }
    }

    if (!selected)
        errText += "No " + alias + " selected" + "\n";

    return errText;
}

/* -----------------------------------------------------------------------------------------
// Converts separate date parts into a date object
//
//   Parameters:
//   day    day (1-31)
//   month  month (1-12)
//   year   year ()
//   Returns Date object for the date parts
*/

function bits_to_date(day, month, year)
{
	return new Date(year, parseInt(month)-1, parseInt(day)+1);
}

/* -----------------------------------------------------------------------------------------
// Splits a Date object into component parts
//
//   Parameters:
//   date   The Date object
//   Returns bits Stringarray containing the day, month and year
*/

function date_to_bits(date)
{
//	alert("date_to_bits(" + date + " )");
	if(date == null)
		return null;

	var dateObj = new Date(date);
	if(dateObj == null)
		return null;

	var bits = new Array();
	bits[0] = "";
	bits[1] = dateObj.getDate();
	bits[2] = parseFloat(dateObj.getMonth() + 1);
	bits[3] = dateObj.getFullYear();
	bits[4] = null
	return bits;
}

/* -----------------------------------------------------------------------------------------
// Validate a date
//
//   Parameters:
//   result Stringarray to hold results
//   bits   Stringarray containing day, month and year
//   Returns result Stringarray to hold error message, day, month, year and whether the year is a leapyear
*/

function validate_date(result, bits)
{
//	alert("validate_date(" + result + ", " + bits + ")");
	var day = bits[0];
	var month = bits[1];
	var year = bits[2];
	var leapyear = false;
	
	if(year/4 == Math.floor(year/4))	
		leapyear = true;	
	if(year/100 == Math.floor(year/100))	
		leapyear = false;
	if(year/400 == Math.floor(year/400))	
		leapyear = true;	

    //oracle cannot accept years outside these ranges
    if (year>9999 || year==0 || year<-4713) 
    	result[0] += " year is invalid";

	if(month < 1 || month > 12)
		result[0] += " month is invalid";

	var months = new Array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	if(leapyear == true && month == 2)
	{
		if (day < 1 || day > 29)
			result[0] += " day is invalid";
	}
	else if(day < 1 || day > months[month])
		result[0] += " day is invalid";	

	result[1] = day;
	result[2] = month;
	result[3] = year;
	result[4] = leapyear;

	return result;
}

/* -----------------------------------------------------------------------------------------
// Validate a short date string (e.g. 31/12/2001)
//
//   Parameters:
//   value   String containing the date, separated by '/'
//   Returns result Stringarray to hold error message, day, month, year and whether the year is a leapyear
*/

function validate_short_date(value)
{
	var result = new Array("");

	var bits = value.split("/");
	if(bits && bits.length == 3)
		return validate_date(result, bits);
	result[0] = "is not a valid date. e.g. 10 May 2000";
	return result;
}

/* -----------------------------------------------------------------------------------------
// Validate a long date string (e.g. 31 dec 2001 or 31-DEC-2001)
//
//   Parameters:
//   date    String containing the date, separated by '/'
//   Returns result Stringarray to hold error message, day, month, year and whether the year is a leapyear
*/

// return bits of dates as well
function validate_long_date(date)
{
// alert("validate_long_date(" + date + ")");
	var value = date.toLowerCase();
	var result = new Array("");

	var split_bits = value.split(" ");
	if(!split_bits || split_bits == null || split_bits.length<3)
	{
		split_bits = value.split("-"); //try for oracle style dates.
		if(!split_bits || split_bits == null || split_bits.length<3)
		{
			result[0] = " is not a valid date. e.g. 10 May 2000";
			return result;
		}
	}

	var new_size = 0;
	var bits = new Array();
	for(var loop = 0; loop < split_bits.length; loop++)
	{
		if(split_bits[loop] != "")
			bits[new_size++] = split_bits[loop];
	}
	
	if(bits && bits.length == 3)
	{
		var month = bits[1];
		var month_names = new Array("jan", "fév", "mar", "avr", "mai", "jun", "jul", "aoû", "sep", "oct", "nov", "déc");
		for(var month_num = 0; month_num < month_names.length; month_num++)
		{
			if(month == month_names[month_num])
			{
				bits[1] = month_num + 1;	// convert to 1 indexed
				return validate_date(result,bits);
			}
		}
	}
	result[0] = " is not a valid date. e.g. 10 May 2000";
	return result;
}

/* -----------------------------------------------------------------------------------------
// Validates a string based on an input mask
//
//   Parameters:
//   alias  Descriptive string to use in an error message
//   text   String to validate
//   mask   String containing input mask
//   Returns String containing error message (if any)
*/

function validate_mask(alias, text, mask)
{
	// ? = any, # = digit, ! = letter	
	var errText = alias + " is invalid, expected format is '" + mask + "'\n    (where # = digit, ! = letter, ? = any).";
	var textNumber = "0123456789";
	var textLetter = "abcdefghijklmnopqrstuvwxyz";	
	
	var textSize = text.length;
	var maskSize = mask.length;
	if(textSize != maskSize)
		return errText;
	
	var lowerText = text.toLowerCase();	
	for(var loop = 0; loop < textSize; loop++)
	{
		if(mask.charAt(loop) == '#')
		{		
			if (textNumber.indexOf(text.charAt(loop), 0) == -1)	// is the matching char a number?
				return errText;
		}
		else if(mask.charAt(loop) == '!')
		{
			if (textLetter.indexOf(lowerText.charAt(loop), 0) == -1)	// is the matching char a letter?
				return errText;
		}
		else if(mask.charAt(loop) == '?')
		{
			// no checking needed
		}
		else
		{
			if (text.charAt(loop) != mask.charAt(loop))	// does it match exactly?		
				return errText;
		}
	}
	return "";
}

/* -----------------------------------------------------------------------------------------
// Validate a string based on a data type
//
//   Parameters:
//   alias  Descriptive string to use in an error message
//   text   String to validate
//   type   String containing data type.  
//          Valid data types are bool, int, num, alpha, alphanum, shortdate, longdate, mask, any
//   Returns String containing error message (if any)
*/

function validate_type(alias, text, type)
{
     if (DEBUG)
        alert("validate_type(" + alias + ", " + text + ", " + type + ")");
        
	var errText = "";
	if(type == "bool")
	{
		if(text != "true" && text != "TRUE" && text != "false" && text != "FALSE")
			errText += alias + " must be either 'true' or 'false'" + "\n";
	}
	else if(type == "int")
	{
		if(validate_chars(text, "0123456789-") == false)
			errText += alias + " must be a whole number.\n";
	}
	else if(type == "num")
	{
		if(validate_chars(text, "0123456789.-") == false)
			errText += alias + " must be a number.\n";
	}
	else if(type == "alpha")
	{
		if(validate_chars(text.toLowerCase(), "abcdefghijklmnopqrstuvwxyz") == false)
			errText += alias + " must be all letters.\n";
	}
	else if(type == "alphanum")
	{
		if(validate_chars(text.toLowerCase(), "abcdefghijklmnopqrstuvwxyz0123456789.-") == false)
			errText += alias + " must be all alphanumeric.\n";
	}
	else if((type == "any") || (type == "mask"))
	{
		// do no character checking
	}
	else if(type == "shortdate")
	{
		if(validate_chars(text, "0123456789/") == false)
			errText += alias + " has invalid date characters.\n";
		else
		{
			var tmpResult = validate_short_date(text);
			if(tmpResult[0] != "")
				errText += alias + tmpResult[0] + "\n";
		}
	}
	else if(type == "longdate")
	{
		// only letters for jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
		if(validate_chars(text.toLowerCase(), "abcdeéfgijlmnoprstuûvy0123456789 -") == false)	
			errText += alias + " has invalid date characters.\n";
		else
		{
			var tmpResult = validate_long_date(text.toLowerCase());
			if(tmpResult[0] != "")
				errText += alias + tmpResult[0] + "\n";
		}
	}
	else	//chars
	{
		if(validate_chars(text, type) == false)	// type contains valid chars
			errText += alias + " contains invalid characters.\n";
	}
	return errText;
}

/* -----------------------------------------------------------------------------------------
// Validate a string based on a permissible length
//
//   Parameters:
//   alias  Descriptive string to use in an error message
//   text   String to validate
//   type   String containing data type (see validate_type for list of data types)
//   min_size int the minimum permissible length for the string
//   max_size int the maximum permissible length for the string
//   Returns String containing error message (if any)
*/

function validate_range(alias, text, type, min_size, max_size)
{
//	alert("validate_range(" + alias + ", " + text + ", " + type + ", " + min_size + ", " + max_size + ")");
	var errText = "";
	if((min_size == null || min_size == "") && (max_size == null || max_size == ""))
		return errText;

	if(type == "int" || type == "num")	// numerical
	{
		// need to count . and - and numerical chars.
		if(count_chars(text, ".") > 1 || count_chars(text, "-") > 1 || count_chars(text, "1234567890") < 1)
			errText += alias + " is not a valid number.\n";
		else
		{
			if(min_size)
			{
				if(parseFloat(text) < parseFloat(min_size))
					errText += alias + " must be " + min_size + " or more\n";
			}
			if(max_size)
			{
				if(parseFloat(text) > parseFloat(max_size))
					errText += alias + " must be " + max_size + " or less\n";
			}
		}
	}
	else if(type == "alpha" || type == "alphanum" || type == "chars" || type == "any")				// text
	{
		if(min_size)
		{
			if(text.length < parseInt(min_size))
				errText += alias + " must be " + min_size + " or more characters long\n";
		}
		if(max_size)
		{
			if(text.length > parseInt(max_size))
				errText += alias + " must be " + max_size + " or less characters long\n";
		}
	}
	else if(type == "shortdate")
	{
		var date_field = validate_short_date(text.toLowerCase());
		var date_val = parseFloat(date_field[3] * 372) + parseFloat(date_field[2] * 31) + parseFloat(date_field[1]);

		if(min_size != "")
		{
			var min_date = null;
			if(min_size.toLowerCase() == "now")
				min_date = date_to_bits(Date());
			else			
				min_date = validate_long_date(min_size.toLowerCase());

			var min_date  = validate_short_date(min_size.toLowerCase());
			if(min_date.length != 5 || min_date[0] != "")
				alert("Error in min date specification : " + min_size);

			var min_val = parseFloat(min_date[3] * 372) + parseFloat(min_date[2] * 31) + parseFloat(min_date[1]);
			if(parseFloat(date_val) < parseFloat(min_val))
				errText += alias + " must not be before " + min_size + "\n";
		}
		if(max_size != "")
		{
			var max_date = null;
			if(max_size.toLowerCase() == "now")
				max_date = date_to_bits(Date());
			else			
				max_date = validate_long_date(max_size.toLowerCase());

			if(!max_date || max_date.length != 5 || max_date[0] != "")
				alert("Error in max date specification : " + min_size);				

			var max_val = parseFloat(max_date[3] * 372) + parseFloat(max_date[2] * 31) + parseFloat(max_date[1]);			
			if(parseFloat(date_val) > parseFloat(max_val))
				errText += alias + " must not be after " + max_size + "\n";
		}
	}
	else if(type == "longdate")
	{
		var date_field = validate_long_date(text.toLowerCase());
		var date_val = parseFloat(date_field[3] * 372) + parseFloat(date_field[2] * 31) + parseFloat(date_field[1]);
		if(min_size != "")
		{
			var min_date = null;
			if(min_size.toLowerCase() == "now")
				min_date = date_to_bits(Date());
			else			
				min_date = validate_long_date(min_size.toLowerCase());

			if(min_date.length != 5 || min_date[0] != "")
				alert("Error in min date specification : " + min_size);
			var min_val = parseFloat(min_date[3] * 372) + parseFloat(min_date[2] * 31) + parseFloat(min_date[1]);
			if(parseFloat(date_val) < parseFloat(min_val))
				errText += alias + " must not be before " + min_size + "\n";
		}

		if(max_size != "")
		{
			var max_date = null;
			if(max_size.toLowerCase() == "now")
				max_date = date_to_bits(Date());
			else			
				max_date = validate_long_date(max_size.toLowerCase());

			if(max_date.length != 5 || max_date[0] != "")
				alert("Error in max date specification : " + max_size);							
			var max_val = parseFloat(max_date[3] * 372) + parseFloat(max_date[2] * 31) + parseFloat(max_date[1]);			
			if(parseFloat(date_val) > parseFloat(max_val))
				errText += alias + " must not be after " + max_size + "\n";
		}
	}
	else if(type == "mask")
	{
		errText += validate_mask(alias, text, min_size);
	}
	return errText;
}

/* -----------------------------------------------------------------------------------------
// Validate a pair of dates
//
//   Parameters:
//   the_form   The form that the controls are contained on
//   first_date_name    The name of the first date control
//   first_date_alias   Descriptive string to use in an error message
//   second_date_name   The name of the second date control
//   second_date_alias  Descriptive string to use in an error message
//   Returns            String containing error message (if any)
*/

// Validates two dates, and if both are populated the second must be >= first, and 2nd cannot be in past
function validate_two_dates(the_form,first_date_name, first_date_alias, second_date_name, second_date_alias)
{
	var errText = "";
	var first_date = get_text(the_form.name, first_date_name);
	var second_date = get_text(the_form.name, second_date_name);

	if(first_date != "") 
		first_date = validate_long_date(first_date);

	if(second_date != "") 
		second_date = validate_long_date(second_date);
			
	if(first_date != null && first_date != "" && first_date[0] != "")
		errText += first_date_alias + first_date[0] + "\n";

	if(second_date != null && second_date != "")
	{
		if(second_date[0] != "")
			  errText += second_date_alias + second_date[0] + "\n";
		else
		{
		  var expiry = bits_to_date(second_date[1], second_date[2], second_date[3]);									
		  if(first_date != null && first_date[0] == "")	  // i.e. populated and valid
		  {
			  var join = bits_to_date(first_date[1], first_date[2], first_date[3]);									
			  if(join > expiry)
				  errText += second_date_alias + " cannot be before " + first_date_alias + ".\n";
		  }
		  var now = new Date();	// do we have a problem with timezones?
		  if(expiry < now)
			  errText += second_date_alias + " cannot be in the past.\n";
		}
	}

	if(errText == "")
		return true;

	alert(errText);
	return false;
}