/**
 * common.js
 *
 * This is a JavaScript library containing commonly used functions
 * that are deployed in web form validation for Visa Signature web
 * application.  The functions used in this library are written to
 * be compatible with Microsoft Internet Explorer 6.0 and also the
 * Mozilla Firefox 1.0.1 web browsers.
 *
 * Part 1 comprise of generic utilities functions which are mainly
 * used for string manipulation and checking.
 *
 * Part 2 comprise of generic AJAX related functions that are used
 * to establish remote asynchronous operations.
 *
 * Part 3 comprise of Visa Signature web application HTML/JSP form
 * input validation functions.
 *
 * @author Charlie Loo (XM Asia-Pacific)
 * @version 1.0.1.20
 * @last updated 7/6/2006 5:30PM
 */


// ***************************************************************
// ** PART ONE: Generic Utilties
// ***************************************************************


/**
 * Given a text string, determine if this text string contains the
 * set of special characters as identified by Visa TS.
 * @param textstring - the text string passed in to be checked
 * @return boolean - true if this text string contains special characters
 */
function hasSpecialCharacters(textstring) {
    var specialstring = "<>\"';()%&";
    if( !textstring ) { return false; }
    if( textstring.length <= 0 ) { return false; }
    for(j=0; j<specialstring.length; j++) {
        if( textstring.indexOf(specialstring.charAt(j)) != -1 ) { return true; }
    }
    return false;
}



/**
 * Given a password string, determine if this string qualifies to
 * be used as a password.  The conditions set by Visa TS are:
 *   1. Cannot contain whitespace in between
 *   2. Cannot contain special characters
 *   3. Mininum length = 6
 *   4. Maximum length = 25
 *   5. Must be alphanumeric characters A-Za-z0-9
 * @param str - the proposed password string
 * @return boolean - true if qualify to be password, false otherwise
 */
function isValidPasswordFormat(str) {
    if( !str ) { return false; }
    if( str==undefined || str==null || trim(str).length==0 ) { return false; }
    
    str = trim(str);
    for(i=0; i<str.length; i++) {
        if( str.charAt(i)==' ' ) { return false; }
    }
    
    if( hasSpecialCharacters(str) ) { return false; }
    if( str.length < 6 || str.length > 25 ) { return false; }
    
    var allow = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for(j=0; j<str.length; j++) {
        if( allow.indexOf( str.charAt(j) )== -1 ) { return false; }
    }

    return true;
}



/**
 * Given a string determine if this string qualifies to be
 * an email address.  This is a very basic check to ensure
 * that the format resembles abc@mydomain.com
 * @param email - the string to be checked
 * @return boolean - true if can be used as email address
 */
function isValidEmailAddressFormat(email) {
    db = false;  //debug flag
	
	var invalidChars = '\/\'\\ ";:?!()[]\{\}^|';
	for (i=0; i<invalidChars.length; i++) {
	    if (email.indexOf(invalidChars.charAt(i),0) > -1) {
	        if (db) { alert('email address contains invalid characters'); }
	        return false;
	    }
	}
	for (i=0; i<email.length; i++) {
	    if (email.charCodeAt(i)>127) {
	        if (db) { alert("email address contains non ascii characters."); }
	        return false;
	    }
	}
	
	var atPos = email.indexOf('@',0);
	if (atPos == -1) {
	    if (db) { alert('email address must contain an @'); }
	    return false;
	}
    if (atPos == 0) {
        if (db) alert('email address must not start with @');
        return false;
    }
    if (email.indexOf('@', atPos + 1) > - 1) {
        if (db) alert('email address must contain only one @');
        return false;
    }
    if (email.indexOf('.', atPos) == -1) {
        if (db) alert('email address must contain a period in the domain name');
        return false;
    }
    if (email.indexOf('@.',0) != -1) {
        if (db) alert('period must not immediately follow @ in email address');
        return false;
    }
    if (email.indexOf('.@',0) != -1){
        if (db) alert('period must not immediately precede @ in email address');
        return false;
    }
    if (email.indexOf('..',0) != -1) {
        if (db) alert('two periods must not be adjacent in email address');
        return false;
    }

    var suffix = email.substring(email.lastIndexOf('.')+1);
    if (suffix.length != 2 && suffix != 'com' && suffix != 'net' && suffix != 'org' && suffix != 'edu' && suffix != 'int' && suffix != 'mil' && suffix != 'gov' & suffix != 'arpa' && suffix != 'biz' && suffix != 'aero' && suffix != 'name' && suffix != 'coop' && suffix != 'info' && suffix != 'pro' && suffix != 'museum') {
        if (db) alert('invalid primary domain in email address');
        return false;
    }
    return true;
}



/**
 * Given a phone number string, check to see if this string qualifies
 * to be a valid phone number.   A valid phone number in this context
 * is of the format nn-nn-nnnnnnn or nn-nnnnnn.
 * Condition:
 *   1. only allows numbers and the '-' character.
 *   2. first character and last character must be a digit
 *   3. tht '-' character min 0 max 2
 *   4. minimum length must be greater than 6
 * @param str - the string to check
 * @boolean - true if str qualifies as a phone number
 */
function isValidPhoneFormat(str) {
    if( !str || !str.length ) { return false; }
    var allow = "0123456789-";
    for(i=0; i<str.length; i++) {
        if( allow.indexOf(str.charAt(i)) == -1 ) { return false; }
    }
    
    firstchar = str.charAt(0);
    lastchar  = str.charAt( str.length - 1 );
    if( !isDigit(firstchar) ) { return false; }
    if( !isDigit(lastchar) )  { return false; }
    
    dashcount = 0;
    for(j=0; j<str.length; j++) {
        if( str.charAt(j)=='-' ) { dashcount++; }
    }
    if( dashcount>2 ) { return false; }
    
    return true;    
}



/**
 * Given a fax number string, check to see if this string qualifies
 * to be a valid fax number.  A valid fax number in this context is
 * the same as that of phone format
 * @param str - the string to check
 * @boolean - true if str qualifies as a fax number
 */
function isValidFaxFormat(str) { return isValidPhoneFormat(str); }



/**
 * Performs left trim of a string to remove whitespaces9947
 * @param str - the string for which whitespaces at left will be removed
 * @return string - the string with whitespaces at left removed
 */
function ltrim(str) {
	if (str==null){return str;}
	for (var i=0; str.charAt(i)==" " || str.charAt(i)=="\n" || str.charAt(i)=="\r" || str.charAt(i)=="\t"; i++);
	return str.substring(i,str.length);
}



/**
 * Performs right trim of a string to remove whitespaces
 * @param str - the string for which whitespaces at right will be removed
 * @return string - the string with whitespaces at right removed
 */
function rtrim(str) {
	if (str==null){return str;}
	for (var i=str.length-1; str.charAt(i)==" " || str.charAt(i)=="\n" || str.charAt(i)=="\r" || str.charAt(i)=="\t"; i--);
	return str.substring(0,i+1);
}



/**
 * Trim leading and trailing whitespaces of a string
 * @param str - the string for which whitespaces at left & right will be removed
 * @return string - the string with whitespaces at left & right removed
 */
function trim(s) { return ltrim(rtrim(s)); }



/**
 * Check to see a character is a digit
 * @param num - character to be checked if it is a digit
 * @return boolean - true if is a digit
 */
function isDigit(num) {
	if (num.length>1){return false;}
	if ("1234567890".indexOf(num)!=-1){return true;}
	return false;
}



/**
 * Check to see if a string is an integer number
 * @param str - string to be checked
 * @return boolean - true if it is an integer number
 */
function isNumeric(sText) {
    if( !sText ) { return false; }
    
    var ValidChars = "0123456789";
    var IsNumber=true;
    var Char;
    
    for (i = 0; i < sText.length && IsNumber == true; i++)  {
        Char = sText.charAt(i);
        if (ValidChars.indexOf(Char) == -1) { IsNumber = false; break; }
    }
    return IsNumber;
}



/**
 * Check to see if a string is alphanumeric string
 * @param val - string to be checked
 * @return boolean - true if it is a number
 */
function isAlphaNumeric(val) {
    if (val.match(/^[a-zA-Z0-9]+$/)) { return true; }
    else{ return false; } 
}


/**
 * Check to see if a string is made up of alphabets only
 * @param val - string to be checked
 */
function isAlphabet(val) {
    if (val.match(/^[a-zA-Z]+$/)) { return true; }
    else{ return false; } 
}



/**
 * Converts a date string of a particular format into
 * a JavaScript Date object.   The current conversion
 * format is valid for dd-mm-yyyy(20-06-2006) and 
 * yyyy-mm-dd(2006-06-20)
 */
function getDateObject(datestr, fmt) {
    var dateobj, sd, sm, sy;
    switch(fmt) {
        case "dd-mm-yyyy" :
            //eg. 20-06-2006
            sd = parseInt(datestr.substring(0, 2),10);
            sm = parseInt(datestr.substring(3, 5),10);
            sy = parseInt(datestr.substring(6),10);
            dateobj = new Date(sy,sm-1,sd);
            break;
        case "yyyy-mm-dd" :
            //eg. 2006-06-20
            sy = parseInt(datestr.substring(0,4),10);
            sm = parseInt(datestr.substring(5,7),10);
            sd = parseInt(datestr.substring(8),10);
            dateobj = new Date(sy,sm-1,sd);
            break;
        default: dateobj = new Date(); break;
    }
    return dateobj;
}




/**
 * Check if input datetime is today datetime
 * @return boolean - true if input datetime is today datetime
 */
function compareDateObjEqualToday(compareDateTime) {
    var dateTimeObj = new Date();
    
    if(dateTimeObj.getYear() == compareDateTime.getYear() && dateTimeObj.getMonth() == compareDateTime.getMonth() && dateTimeObj.getDate() == compareDateTime.getDate())
    {
    	return true;
    }
   
    return false;    
}


/**
 * returns a JavaScript time object but
 * 
 */
 
function DateEx( dateobjid, timeobjid, datefmt )
{
	this.compareTo = function( date )
	{
		if ( this.hourType != 'Normal' && this.date.getFullYear() == date.getFullYear() && this.date.getMonth() == date.getMonth() && this.date.getDate() == date.getDate() )
		{
			if ( this.hourType == "Morning" )
			{
				if ( date.getHours() < 12 )
				{
					return 0;
				}
				else
				{
					return -1;
				}
			}
			else
			if ( this.hourType == "Any" || this.hourType=="00:00" || this.hourType == "Evening")
			{
				return 0;
			}
			else			
			if ( this.hourType == "Afternoon" )
			{
				if ( date.getHours() < 12 )
				{
					return 1;
				}
				else
				if ( date.getHours() < 18 )
				{
					return 0;
				}
				else
				{
					return -1;
				}
			}		
		}
		else
		{	
			return this.date > date ? 1 : this.date < date ? -1 : 0;
		}
	}
	this.date = getDateObject(document.getElementById(dateobjid).value, datefmt);
	var hour = document.getElementById(timeobjid).value;	
	if( hour=='Morning' || hour=='Any' || hour=='Afternoon' || hour=='Evening' || hour=="00:00")
	{
		this.hourType = hour;
	}
	else 
	{
		this.hourType = 'Normal';
		this.date.setHours(getHourFromTimeString(hour));
		this.date.setMinutes(0,0,0)
	}
}



/**
 * returns a JavaScript date object of current date but
 * with the hour, minute, second and milliseconds reset
 * to 0
 */
function getCuurentDateOnlyObject() {
    var dateobj = new Date();
    dateobj.setHours(0,0,0,0);
    return dateobj;
}


/**
 * Given a time string of the format hh:mm:ss, obtain the hour
 * component of this time string.  Eg. 20:48:10 returns 20 and
 * 07:55:12 returns 07.  The time separator must be colon :
 */
function getHourFromTimeString(timestr) {
    if( !timestr || trim(timestr)=="" ) {
        //alert("error.getHourFromTimeString.invalidparam");
        return "0";
    }
    var pos = timestr.indexOf(":");
    if( pos <= 0 || pos>2 ) {
        //alert("error.getHourFromTimeString.badtimestring");
        return "0";
    }
    else {
        hourstring = timestr.substring(0,pos);
        if( isNumeric(hourstring) ) { return hourstring; }
        else { return "0"; }       
    }
}


/**
 * Given an object, determine if this object is an array
 */
function isArray(ob) {
    if( !ob ) { return false; }
    return (ob) && (typeof ob == 'object') && (ob.constructor==Array) ;
}



// ***************************************************************
// ** PART TWO: AJAX Utilties
// ***************************************************************


/*
 * Returns a new XMLHttpRequest object, or 
 * false if this browser doesn't support it
 */
function getXMLHttpRequest() {
    var xmlreq = false;
    if (window.XMLHttpRequest) {
        // Create XMLHttpRequest object in non-Microsoft browsers
        xmlreq = new XMLHttpRequest();
    }
    else if (window.ActiveXObject) {
        try {
            // Try to create XMLHttpRequest in later versions of Internet Explorer
            xmlreq = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e1) {
            try {
                // Try version supported by older versions of Internet Explorer
                xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                // Unable to create an XMLHttpRequest with ActiveX
            }
        }
    }
    return xmlreq;
}
  

/**
 * Returns a function that waits for the specified XMLHttpRequest
 * to complete, then passes it XML response to the given handler function.
 * req - The XMLHttpRequest whose state is changing
 * responseXmlHandler - Function to pass the XML response to
 */
function getReadyStateHandler(req, responseXmlHandler) {

   // Return an anonymous function that listens to the XMLHttpRequest instance
   return function () {

     // If the request's status is "complete"
     if (req.readyState == 4) {
       
         // Check that we received a successful response from the server
         if (req.status == 200) {

             // Pass the XML payload of the response to the handler function.
             responseXmlHandler(req.responseXML);

         } else {

             // An HTTP problem has occurred
             alert("HTTP error " + req.status + ": " + req.statusText);
         }
     }
     
   }
}



// ***************************************************************
// ** PART THREE: Visa Signature Web App Generic Validators
// ***************************************************************


/**
 * A general function to determine if a particular dropdown list has
 * an item that has been selected.  It assumes that if no item is
 * selected, then the value obtained from the selected index is empty
 * Typically used by { salutation, country list, currency list, etc}
 * @param oid - the ID of the dropdownlist object
 * @param errMessage - the error message to display if none selected
 */
function isDropDownListSelected(oid, errMessage) {
    var selectedDropDownListItem = document.getElementById(oid).options[document.getElementById(oid).selectedIndex];
    if( selectedDropDownListItem.value=="" ) {
        alert(errMessage);
        document.getElementById(oid).focus();
        return false;
    }
    else { return true; }
}


/**
 * A general function to validate that a field contains a valid
 * person's name.  The name is from A-Za-z inclusive of space 
 * only.  Any other characters inclusive digits are not allowed
 * and not recognized to be a valid person name.
 * @param oid - the ID of the text field object
 * @param errMessageBlank - text field empty err msg
 * @param errMessageBlank - text field contain digits err msg
 */
function isPersonNameString(oid, errMessageBlank, errMessageFmt) {
    var obj = document.getElementById(oid);
    if( trim(obj.value)=="" ) {
        alert(errMessageBlank);
        obj.value = "";
        obj.focus();
        return false;
    }
    else {
        var s   = trim(obj.value);
        var bad = "abcdefghijklmnopqrstuvwxyz -ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        for(i=0; i<s.length; i++) {
            if( bad.indexOf(s.charAt(i) )==-1 ) {
                alert(errMessageFmt);
                obj.focus();
                return false;
            }
        }
        return true;
    }
}


/**
 * A function just to check that the value of a text field is non empty
 */
function isNonEmptyField(oid, errMessageBlank) {
    var obj = document.getElementById(oid);
    if( trim(obj.value)=="" ) {
        alert(errMessageBlank);
        obj.value = "";
        obj.focus();
        return false;
    }
    return true;
}


/**
 * A function to determine if a given field contains number
 */
function isNumericValueOnly(oid, errMessageFmt) {
    var obj = document.getElementById(oid);
    if( isNumeric( trim(obj.value) ) ) { return true; }
    else {
        alert(errMessageFmt);
        obj.focus();
        return false;
    }
}


/**
 * Check a group of radio buttons to ensure that at one exclusive
 * option has been chosen
 * @param arr - array containing the IDs of the group of radio button options
 * @param errMessage - message to alert when none is chosen
 */
function isAtLeastOneExclusiveOptionChosen(arr, errMessage) {
    for(j=0; j<arr.length; j++) {
        if( document.getElementById(arr[j]).checked == true ) { return true; } 
    }
    alert(errMessage);
    return false;
}


/**
 * Check email address validity.  Must not be blank and must be of
 * a valid format
 */
function isValidEmail(oid, errMessageBlank, errMessageFmt) {
    var email = document.getElementById(oid);
    if( trim(email.value)=="" ) {
        alert(errMessageBlank);
        email.value = "";
        email.focus();
        return false;
    }
    else {
        if( !isValidEmailAddressFormat(email.value) ) {
            alert(errMessageFmt);
            email.focus();
            return false;
        }
        else { return true; }
    }
}


/**
 * Check for valid phone and fax number.  A valid phone and fax
 * number is non empty and is of the format <ccode>-<acode>-<number>
 * @see isValidPhoneFormat
 */
function isValidPhoneNo(oid, errMessageBlank, errMessageFmt) {
    var phone = document.getElementById(oid);
    if( trim(phone.value)=="" ) {
        alert(errMessageBlank);
        phone.value = "";
        phone.focus();
        return false;
    }
    else {
        if( !isValidPhoneFormat(trim(phone.value)) ) {
            
            alert(errMessageFmt);
            phone.focus();
            return false;
        }
        else { return true; }
    }
}


/**
 * inspect the field to ensure that the login credential
 * value is non empty, of acceptable length and of the
 * acceptable format
 */
function isValidLoginCredentialString(oid, errMessageBlank, errMessageLen, errMessageFmt) {
    var obj = document.getElementById(oid);
    // check blank first
    if( trim(obj.value)=="" ) {
        alert(errMessageBlank);
        obj.focus();
        return false;
    }
    // check length next
    if( trim(obj.value).length<6 || trim(obj.value).length>25 ) {
        alert(errMessageLen);
        obj.focus();
        return false;
    }
    // check it contains only alphanumeric
    if( !isAlphaNumeric( trim(obj.value) ) ) {
        alert(errMessageFmt);
        obj.focus();
        return false;
    }
    return true;
}


/**
 * inspect the field to ensure that the login credential
 * value is non empty, of acceptable length and of the
 * acceptable format
 */
function isValidLoginCredentialString2(oid, errMessageBlank, errMessageLen, errMessageFmt) {
    var obj = document.getElementById(oid);
    // check blank first
    if( trim(obj.value)=="" ) {
        alert(errMessageBlank);
        obj.focus();
        return false;
    }
    // check length next
    if( trim(obj.value).length<6 || trim(obj.value).length>25 ) {
        alert(errMessageLen);
        obj.focus();
        return false;
    }
    // check it contains only alphanumeric
    if( !isAlphaNumeric( trim(obj.value) ) ) {
        for( i=0; i<obj.value.length; i++ ) {
            if( obj.value.charAt(i) == " " ) {
            	return true;
            }
        }
        alert(errMessageFmt);
        obj.focus();
        return false;
    }
    return true;
}


/**
 * inspect the field to ensure that the login credential (password)
 * value is non empty, of acceptable length and of the
 * acceptable format
 */
function isValidLoginCredentialPWString(oid, errMessageBlank, errMessageLen, errMessageFmt) {
    var obj = document.getElementById(oid);
    // check blank first
    if( trim(obj.value)=="" ) {
        alert(errMessageBlank);
        obj.focus();
        return false;
    }
    // check length next
    if( trim(obj.value).length<7 || trim(obj.value).length>25 ) {
        alert(errMessageLen);
        obj.focus();
        return false;
    }
    // check it contains only alphanumeric
    if( !isAlphaNumeric( trim(obj.value) ) ) {
        alert(errMessageFmt);
        obj.focus();
        return false;
    }
    return true;
}


/**
 * Additional requirement made mandatory by security team
 * A password must comprise of both alphabets and numeric
 */
function fulfillPasswordRequirement(oid, errMessage) {
    var obj = document.getElementById(oid);
    var pwd = trim(obj.value);
    var num = false;
    var alp = false;
    for(i=0; i<pwd.length; i++) {
        if( isDigit(pwd.charAt(i)) )    { num = true; }
        if( isAlphabet(pwd.charAt(i)) ) { alp = true; }
    }
    if( !(num==true && alp==true) ) {
        alert(errMessage);
        obj.focus();
        return false;
    }
    
    return true;
}


/**
 * Check to determine if a particular string is a valid BINNO
 */
function checkBinno(oid, errMessageBlank, errMessageLen, errMessageFmt) {
    var binno = document.getElementById(oid);
    if( trim(binno.value)=="" ) {
        alert(errMessageBlank);
        binno.value = "";
        binno.focus();
        return false;
    }
    else {
        if( trim(binno.value).length < 9 ) {
            alert(errMessageLen);
            binno.focus();
            return false;
        }
        if( !isNumeric(binno.value) ) {
            alert(errMessageFmt);
            binno.focus();
            return false;
        }
        return true;
    }
}



/**
 * This function inspect a series of text fields and text area
 * for special characters.  The moment the first special character
 * is detected, an alert message will be raised, the field will
 * gain focus, the rest of the check will be stopped and the
 * function return false.
 *
 * If no text fields contain special characters, the function ends
 * and returns a true value
 */
function textFieldsAreSafe(farray, errMessage) {
    if( isArray(farray) ) {
        for(i=0; i<farray.length; i++) {
            var fieldentry = document.getElementById( farray[i] );
            if( hasSpecialCharacters(fieldentry.value) ) {
                alert(errMessage);
                fieldentry.focus();
                return false;
            }
        }
        return true;
    }
    else {
        alert("error.textFieldsAreSafe.array.undefined");
        return false;
    }
}


/**
 * This function prepares a date object by capturing the date string
 * referenced by the dateobjid, capturing the time string referenced
 * by the timeobjid and using the format specified by datefmt.
 */
function prepareDate( dateobjid, timeobjid, datefmt ) {
    var thedate = getDateObject(document.getElementById(dateobjid).value, datefmt);
    var hour = document.getElementById(timeobjid).value;
    if( hour=='Morning' || hour=='Any' ) { hour="00"; }
    else if( hour=='Afternoon' ) { hour="12"; }
    else if( hour=='Evening' ) { hour="18"; }
    else { hour = getHourFromTimeString(hour); }
    thedate.setHours(hour);
    return thedate;
}

/**
 * Given a textarea string, determine if this textarea string 
 * exceeded 300 characters
 * @param textstring - the text string passed in to be checked
 * @return boolean - true if this text string contains special characters
 */
function checkStringSize(oid, lenlimit, errMessage) {
    var obj = document.getElementById(oid);
    if( trim(obj.value).length > lenlimit ) {
       	alert(errMessage);
    	return false; 
    }
   
   return true;    
}

