/* version 2 - 
	implement simple regular expression validation and, length validation
	implement separate function to get control's value (not radio or checkbox yet)
	add textarea control type to function sq_controlValue
*/


/* version 3 - GLB Aug 31, 04
	validation of in-list editing requires targeting multiple rows for the same
	field - therfore validation loop now loops through form fields and allows
	setting the golbal variable SQVAL_MATCH_EXACT to determine whether
	a field name must be identical to the spec name to be validated, or,
	whether the spec name may simply be /contained/ in the field name.
	
	Example:
	spec name = "myfield" 
	form fields = "myfield", "myfield1", "myfield2","myfieldn", "yourfield"

	If SQVAL_MATCH_EXACT = true then only "myfield" will be validated 

	If SQVAL_MATCH_EXACT = false then all the fields except "yourfield"
	will be validated using the "myfield" spec

*/

SQVAL_MATCH_EXACT 	= true			// default

/* Constants for defining 'allowEmpty' parameter in validation specs */
SQVAL_EMPTYOK		= true
SQVAL_NOTEMPTY	= false


/* 	regular expression constants
	please use consistent naming conventions (upper case, prefix with SQVAL_ ) 
	and ensure that they /work/ before publishing them here
*/

var SQVAL_HTTP_OR_HTTPS	= "(http://|https://)\\w+"
var SQLVAL_FILE_PATH = "^\\w+\\.\\w{3,4}$"
var SQLVAL_EMAIL = "[\\w-_\\.]*[\\w-_\\.]\\@[\\w-_\\.]+\\.\\w+"
var SQLVAL_HTTP_OR_HTTPS_OR_FTP_OR_EMAIL = "^(http://|https://|ftp://|[\\w-_\\.]*[\\w-_\\.]\\@[\\w-_\\.]+\\.)\\w+$"
var SQLVAL_DATE = "^\\d\\d\\/\\d\\d\\/\\d\\d\\d\\d$" //dd/mm/yyyy
var SQLVAL_2_OR_3_LETTERS = "^[A-Z]{2,3}$"


var SQLVAL_2NUMBERS_OR_2NUMBERS_WITH_DECIMAL_PLACES = "^[0-9]{1,2}$|^[0-9]{1,2}\\.[0-9]{1,2}$" //Ideal for Percentages

//var SQLVAL_2NUMBERS_OR_2NUMBERS_WITH_DECIMAL_PLACES = "^\\d{1,2}(\\.\\d{1,2})*$"


var SQLVAL_3NUMBERS = "^[0-9]{1,3}$" //Ideal for Checking Age
var SQLVAL_4NUMBERS_OR_4NUMBERS_WITH_DECIMAL_PLACES = "^[0-9]{1,4}$|^[0-9]{1,4}\\.[0-9]{1,2}$" //Ideal for Money
var SQLVAL_5NUMBERS_OR_5NUMBERS_WITH_DECIMAL_PLACES = "^[0-9]{1,5}$|^[0-9]{1,5}\\.[0-9]{1,2}$" //Ideal for Money
var SQLVAL_6NUMBERS_OR_6NUMBERS_WITH_DECIMAL_PLACES = "^[0-9]{1,6}$|^[0-9]{1,6}\\.[0-9]{1,2}$" //Ideal for Money
var SQLVAL_8NUMBERS ="^[0-9]{1,8}$" 		//Idealy for money that does not need decimal places e.g 500000


function sq_controlValue(oCtl){
	var vValue
	switch(oCtl.type.toUpperCase()){
		case "SELECT" :
		case "SELECT-ONE" :
			vValue = oCtl[oCtl.selectedIndex].value
			break
		case "TEXT" :
		case "TEXTAREA" :
		case "PASSWORD" :
			vValue = oCtl.value
			break

		// radio and checkbox not yet implmented
		case "RADIO" :
		case "CHECKBOX" :
		default:
			vValue = ""
			break
	}
	return vValue
}

function trim(s) {
  while (s.substring(0,1) == ' ') {
    s = s.substring(1,s.length);
  }
  while (s.substring(s.length-1,s.length) == ' ') {
    s = s.substring(0,s.length-1);
  }
  return s;
}


function sq_ControlHasData(sControl){
	var bOK, idx, bChecked
	var sFormName = g_sq_FormName
	bOK 		= true

	switch(sControl.type.toUpperCase()){
		case "SELECT":
		case "SELECT-ONE":
			idx 	= sControl.selectedIndex
			if (sControl[idx].value == '') bOK = false
			break
		case "TEXT":
		case "TEXTAREA":
		case "PASSWORD":
			if (sControl.value == '') bOK = false
			break
		case "RADIO":
			bChecked = false
			for (var i=0; i < sControl.length; i++){
				if (sControl[i].checked) bChecked=true
			}
			if (!bChecked) bOK = false
			break
		case "CHECKBOX":
			if (!sControl.checked) bOK = false
			break
		default:
			bOK = false
			break
	}
	return bOK
}

function sq_IsInteger(sControl){
	var idx,sValue
	var numbers = "0123456789"

	sValue = sq_controlValue(sControl)
	if(sValue.length == 0){
		sValue="x"
	}
	for(var i=0;i!=sValue.length;i++){
		if(numbers.indexOf(sValue.charAt(i)) < 0) return false
	}
	return true
}

function isnumbetween ( s, min, max ){
	s = parseFloat(s)
	return ( ( s >= ( min != null ? min : Number.MIN_VALUE ) )
		&& ( s <= ( max != null ? max : Number.MAX_VALUE ) ) );
}

function checkdate(){
	var iDay,iMonth,iYear
	var aParts = checkdate.arguments[2].split(checkdate.arguments[1])
	var bOK = false
	if(aParts.length == 3){
		switch(checkdate.arguments[0]){
			case "DDMMYYYY" :
				iDay	= aParts[0]
				iMonth	= aParts[1]
				iYear	= aParts[2]
				break
			case "MMDDYYYY" :
				iDay	= aParts[1]
				iMonth	= aParts[0]
				iYear	= aParts[2]
				break
			case "YYYYMMDD" :
				iDay	= aParts[2]
				iMonth	= aParts[1]
				iYear	= aParts[0]
				break
			default:
				return bOK
				break
		}
		var sDays 	= "0,31,28,31,30,31,30,31,31,30,31,30,31"
		var aDays 	= sDays.split(",")
		if( isnumbetween(iYear,1900,9999) && isnumbetween(iMonth,1,12) ){
			var iMaxDays = aDays[iMonth]
			if (iMonth==2 && (((iYear%4==0)&&(iYear%100 != 0)) || (iYear%400==0)) ) iMaxDays = 29  //leap year
			if(isnumbetween(iDay,1,iMaxDays)) bOK = true				// <<< here is where we find out
		}
	}
	return bOK
}


// constructor for our custom object
function sq_control(bAllowEmpty,sName,sType,sValidation,sMsg){
	this.allowEmpty = bAllowEmpty
	this.name = sName
	this.type = sType.toUpperCase()
	this.validation = sValidation.toUpperCase()
	this.errMsg	= sMsg

	// some validation types have additional parameters
	if(sq_control.arguments.length > 5){
		switch(this.validation){
			case "RANGE" :
				this.min	 		= sq_control.arguments[5]
				this.max			= sq_control.arguments[6]
				break
			case "DATE" :
				this.format		= sq_control.arguments[5].toUpperCase()
				this.seperator	= sq_control.arguments[6]
				break
			case "EXPRESSION" :
				this.pattern		= sq_control.arguments[5]
				this.flags		= sq_control.arguments[6]
				break
			case "LENGTH" :
				this.min			= sq_control.arguments[5]
				this.max			= sq_control.arguments[6]
				break
			case "PASSWORD" :
				this.confirmName	= sq_control.arguments[5]
				break
			case "NOTEMPTYIF" :
				this.testName	= sq_control.arguments[5]
				break
		}
	}
}	

// array of custom controls
var sq_oControls = new Array()

//global variable for form name
var g_sq_FormName


// called by the form when submitted
function sq_submit(sThisForm){


	var sControl,sMinVal,sMaxVal,thisValue
	var bValidateThis,iSpecIx,bOK
	
	g_sq_FormName = sThisForm
	var sFormName = g_sq_FormName

	// version 3 - check all form fields for a validate spec
	var oForm 	= eval("document." +sFormName)
	for(var iField=0;iField<oForm.length;iField++){				// for each form field

		sControl			= eval("document." +sFormName +"." + oForm[iField].name)
		if(sControl.type	!= "hidden"){									// do not validate hidden fields
			thisValue 	= sq_controlValue(sControl)				// user input value
			for(var i=0;i<sq_oControls.length;i++){					// test each validation spec

				/*	validation is to be performed IF
						this.allowEmpty = false
						this.allowEmpty = true and thisValue.length > 0 */

				bValidateThis = (!sq_oControls[i].allowEmpty || thisValue.length > 0)

				if( (SQVAL_MATCH_EXACT && sq_oControls[i].name == sControl.name && bValidateThis) 
					|| 
					(!SQVAL_MATCH_EXACT && sControl.name.indexOf(sq_oControls[i].name)==0 && bValidateThis))
				{
//alert("have control named " +sControl.name)
//alert("validating "+sControl.name)
//alert("val spec name=" +sq_oControls[i].name)
//alert("indexOf=" +sControl.name.indexOf(sq_oControls[i].name))

					bOK	= true
					
					switch(sq_oControls[i].validation.toUpperCase()){
						case "PASSWORD" :
							var confirm = eval("document." +sFormName +"." + sq_oControls[i].confirmName)
							if(confirm.value != thisValue) bOK = false
							break
	
						case "NOTEMPTYIF" :
							var testControl = eval("document." +sFormName +"." + sq_oControls[i].testName )	
							var testValue = sq_controlValue(testControl)
							if(testValue!="" && thisValue =="") bOK = false
							break
	
						case "EXPRESSION"  :
							var oRegex = new RegExp(sq_oControls[i].pattern, sq_oControls[i].flags)
							if(!oRegex.test(thisValue)) bOK = false
							break
	
						case "LENGTH" :
							if(thisValue.length < sq_oControls[i].min || thisValue.length > sq_oControls[i].max){
								bOK = false
							}
							break
	
						case "NOTEMPTY" :
							if(!sq_ControlHasData(sControl)) bOK = false
							break
	
						case "INTEGER" :
							if(!sq_IsInteger(sControl)) bOK = false
							break
	
						case "EMPTY" :
							if(sq_ControlHasData(sControl)) bOK = false							
							break
	
						case "DATE" :
							if(!checkdate(sq_oControls[i].format,sq_oControls[i].seperator,thisValue)) bOK = false
							break
	
						case "DAY" :
							if(!sq_IsInteger(sControl)) {
								bOK = false;
								break;
							}
							if(thisValue < 1 || thisValue > 31 ) bOK = false							
							break
	
						case "MONTH" :
							if(!sq_IsInteger(sControl)) {
								bOK = false;
								break;
							}
							if(thisValue < 1 || thisValue > 12 ) bOK = false							
							break
	
						case "RANGE" :
							if(thisValue < sq_oControls[i].min || thisValue > sq_oControls[i].max ) bOK = false							
							break

					}	// end switch

					// if we failed then say why and call a halt
					if(!bOK){
						alert(sq_oControls[i].errMsg)
						setFocus(sControl)
						return false
					}
				
				}		// end validation IF
			}			// end validation specs loop
		}  				// end hidden field IF
	}					// end form fields loop
	return true
}

function setFocus(sControl){
	if (sControl.type.toUpperCase() == "RADIO"){
		sControl[0].focus()
	}else{
		sControl.focus()
	}
	return
}