//Terra-Virtua's Fabulous Forms-handling Javascripts
//All contents (C) 2003 Larry Groebe/Terra-Virtua.

//globals for live (charcount) tips
TV_tipTimer=0
TV_tipObject=0
TV_tipText=0

//globals for forms validation
TV_required=new Array()
TV_required.optional=false
TV_required.message="The '{name}' field is required to complete the form."

TV_mmddyyyy=new Array()
TV_mmddyyyy.type="date"
TV_mmddyyyy.regex= /^(\d\d?)\D+(\d\d?)(\D+(\d\d\d?\d?))?$/
TV_mmddyyyy.result='{1}*1+"/" + {2}*1 + "/"+(!{4}?2002:({4}<50?{4}*1+2000:({4}<100?{4}*1+1900:{4})))'
TV_mmddyyyy.internal="new Date({4},{1},{2})"
//----
TV_bigbucks=new Array()
TV_bigbucks.regex= /^\$?(([1234567890.]+.*)[Dd]ollars?|([1234567890.]+.*))/
TV_bigbucks.result='"$"+{2}+{3}'
TV_bigbucks.message="Dollar amount is invalid"
//----
TV_yyyy=new Array()
TV_yyyy.regex= /^(\d\d\d?\d?)$/
TV_yyyy.result='!{1}?2002:({1}<50?{1}*1+2000:({1}<100?{1}*1+1900:{1}))'
//----
TV_percent=new Array()
TV_percent.regex= /^(\d\d?\d?)%?$/
TV_percent.result='{1}'
TV_percent.type="int"
TV_percent.min=0
TV_percent.max=100
//----
TV_integer=new Array()
TV_integer.type="int"
TV_integer.regex= /^(\d*)$/
TV_integer.result='{1}'
TV_integer.min=0
TV_integer.max=32000
//----
TV_phone=new Array()
TV_phone.regex=/^[\(]?([0-9]{0,3})[-| |\)]+([0-9]+)[-| ]?([0-9]+)$/
TV_phone.result='{1}+"-"+{2}+"-"+{3}'
//----
TV_ssnum=new Array()
TV_ssnum.regex=/^([0-9]{3})[-| )]?([0-9]{2})[-| ]?([0-9]{4})$/
TV_ssnum.result='{1}+"-"+{2}+"-"+{3}'

//----
TV_email=new Array()
TV_email.regex= /^.+@.+\..+$/
TV_email.message= "'{name}' is not a valid email address. Please correct it."
//----
TV_web=new Array()
TV_web.regex= /^((http:\/\/)?)(.+\..+)$/
TV_web.result='{3}'

var TV_errormsg=''

//------------------
//--V1.10  6/19/02 LG morew accurate tip location; lose the smoothscroll - too slow!
//--V1.00a 5/16/02 LG performs live charactercounting in a tooltip
//-- Use this tooltip and this scripting on form objects:
//--<div id="TV_ccTip" style="position:absolute; left:1px; top:1px; z-index:1; overflow: hidden; width: 80px; height: 18px; visibility: hidden;"><table cellpadding=1 class="tooltip"><tr><td><font style="font-family:arial; font-size:10px; font-weight:bold">Size: <span id="TV_ccTipData">1</span> char</font></td></tr></table></div>
//--onFocus="TV_startTip(this,event)" onBlur="TV_stopTip(this,event)"  onKeyUp="TV_tipText.innerHTML=this.value.length">

function TV_startTip(me,evt) {
 evt=(evt)?evt:(window.event)?window.event:"" //see goodman book for explanation
 if (!evt) {return}
 TV_tipObject=document.getElementById('TV_ccTip').style  //short global refernces to our objects
 TV_tipText=document.getElementById('TV_ccTipData')
 TV_tipText.innerHTML=me.value.length
 object=me
 loffset=0
 toffset=0
 //climb through all the parents of this object to find it's true location
 while (object) {
	if (app=="M5"&&!object.offsetParent.offsetParent){break}
 	loffset=loffset+object.offsetLeft
 	toffset=toffset+object.offsetTop
	object=object.offsetParent
	}
 TV_tipObject.height=18
 TV_tipObject.left=loffset
 TV_tipObject.top=toffset - parseInt(TV_tipObject.height)
 TV_tipObject.visibility="visible"
// *sigh* ... different positioning methods for IE and NN
// TV_tipObject.left= (evt.offsetX>=0)? evt.clientX-evt.offsetX+2 : evt.target.offsetLeft+2
// TV_tipObject.top= (evt.offsetY>=0)? evt.clientY-evt.offsetY-4 : evt.target.offsetTop-4
TV_tipTimer=setInterval("TV_moveTip()",10)
}

 function TV_moveTip() {
// TV_tipObject.top=parseInt(TV_tipObject.top)-18
// TV_tipObject.height=parseInt(TV_tipObject.height)+18
// if (parseInt(TV_tipObject.height)>=18){clearInterval(TV_tipTimer)}
clearInterval(TV_tipTimer)
 }

function TV_stopTip() {
 clearInterval(TV_tipTimer)
 TV_tipObject.visibility="hidden"
 }


//-------------------
//LG 05/30/2002 set all form fields to readonly/disabled...client, not server loaded 
function TV_disableForms() {
	myform=document.forms[0]
	cnt=myform.elements.length
	for (i=0; i<cnt;i++) {
		myform.elements[i].disabled=true
		myform.elements[i].readonly=true
		}
	}


//-------------------
//LG 02/12/2004 disabled the ID test for now -- too many fields now have IDs for other reqasons
//LG 07/01/2002 fields can have IDS which are matched against cookie to NOT validate (if, say, hidden)
//LG 06/19/2002 focus (theoretically) returned to bad field
//LG 05/31/2002 "message" is optional; fields now presumed required unless "optional" is stated.
//LG 05/22/2002 An elegant blend of power and compactness, I think.

TV_inUse=false

function TV_validateAll(myform) {
//run through all known fields in the form so that ValidateForm can also work
//but only those fields which match our cookie'd "interests"
  TV_errormsg=''
  cnt=myform.elements.length
  smatch=document.cookie.match(/interest=(.{3,6})!/)
  for (x=0; x<cnt;x++) {
	  myfield=myform.elements[x]
	  fieldname=myfield.name.split(":")[0] //get rid of any Zope pseudoname stuff
	  if (validation[fieldname]) {
//		if ((myfield.id=="")||((smatch!="")&&(myfield.id.indexOf(smatch[1])>-1))) { 
//			alert ("validating...")
	  		success=TV_validateItem(myfield)
			if (!success) {badfield=myfield}
//			}
	  	}
	  }  
  if (TV_errormsg>'') {
	  alert ("The form couldn't be submitted due to the following errors:\r" + TV_errormsg + "\n")
	  badfield.focus()
	  return false
 	  }
  return true	
  }


function TV_validate(thingee) {
//single-field wrapper for validateItem, so that validateAll can also work
  if (TV_inUse){return}
  TV_inUse=true
  TV_errormsg=''
  if (!TV_validateItem(thingee)) {
  	alert (TV_errormsg+"\n")
  	thingee.focus()
	}
  TV_inUse=false
  }

// 8/19/03 LG - fixed optional when it's a feildname; also added a matchfield '<>' type which looks to see if the two fields are mutually-exclusively-blank  
//7/15/02 LG MATCHFIELD can be "don't match"; OPTIONAL a field that must also be blank; radio buttons sorta in place 
//6/7/02 LG now with MATCHFIELD for text fields to test against one another
function TV_validateItem(me) {
  fieldname=me.name.split(":")[0] //get rid of any Zope pseudoname stuff
  displayname=fieldname.replace("_"," ") //also clean up the underscores
  //	alert ("validating " + fieldname)

  if (!validation[fieldname]) {
 	TV_errormsg=TV_errormsg + "WARNING: the " + displayname + " field has no validation template"
 	return false
 	} 

  vspec=validation[fieldname]		//assigning this shortens & speeds up subsequent code
  //error msg depends if ALL we're doing is required field, or OK with a default msg 
  if (typeof vspec=="string") {
 	  vmsg=vspec
  } else {  
	  vmsg= (vspec.message)?vspec.message:"The '{name}' field is incorrect; please fix it and submit again."
  }
  vmsg=vmsg.replace("{name}",displayname)
  vmsg=vmsg.replace("{min}",eval(vspec.min))
  vmsg=vmsg.replace("{max}",eval(vspec.max))
  vmsg="\r"+vmsg
  //decide if we're looking at a SELECT element, RADIO buttons, or something else
  //NOTE WE SHOULD INSTEAD TRY TO GET VALUE out of the DIFFERENT TYPES, THEN PROCESS IT ALIKE 
  myvalue=''
  switch (me.type.substring(0,3)) {
  case 'sel': //SELECT STATEMENTS
  case 'rad':
    if (me.type.substring(0,3)=='sel') {
    		myvalue=me[me.selectedIndex].value
    	} else {
			for (z=0; z<me.form.elements[me.name].length; z++) {
				if (me.form.elements[me.name][z].checked) { myvalue= me.form.elements[me.name][z].value }
//				alert (document.forms[me.form.name].elements[me.name][z].value + " is " + document.forms[me.form.name].elements[me.name][z].checked)
			 }
    	}
    	
  	if (vspec.matchfield) {
  	  element2=me.form.elements[vspec.matchfield]
  	  if (element2.type.substring(0,3)=='sel') {
  	  	altvalue=element2.options[element2.selectedIndex].value
  	  	} else {
  	  	altvalue=element2.value }
  	  success = (myvalue==altvalue)
  	  if (vspec.matchtype) {
  	  	if (vspec.matchtype=='!') {success= 1-success}
  	  	}
	  if (!success) {
		TV_errormsg=TV_errormsg + vmsg		//didn't match; don't pass 
		return false
		}
	  }

  	if ((myvalue=='') || (myvalue==null)) {
	  if (vspec.optional) {
 		if (vspec.optional==true) {return true}	//that's OK...we're allowed to be blank.
		element2=me.form.elements[vspec.optional]
		vmsg=vmsg.replace("{optional}",vspec.optional)
	  	if (element2.type.substring(0,3)=='sel') {
  	  		altvalue=element2.options[element2.selectedIndex].value
 	 	  	} else {
  		  	altvalue=element2.value }
		if (altvalue>'') {
			TV_errormsg=TV_errormsg + vmsg 
			return false}	//our trigger value isn't blank but we are - bad news
		}
	  }

	tval=0
	for (i=0; i<me.length; i++) {
		tval = tval + (me.options[i].selected && me.options[i].value>'') }
	tmin = (vspec.min)? eval(vspec.min): 0 
	tmax = (vspec.max)? eval(vspec.max): me.length
	tmin = (vspec.min)? tmin : (vspec.max) ? tmin: 1	//neither min nor max?then required 
	if ((tval>tmax)||(tval<tmin)) {
		TV_errormsg=TV_errormsg + vmsg			//it didn't pass 
		return false
		}
	return true
    break
  	
  default:  //TEXT BOXES & TEXTAREAS
	//should we match another field?
  	if (vspec.matchfield) {
  	  element2=me.form.elements[vspec.matchfield]
  	  if (element2.type.substring(0,3)=='sel') {
  	  	altvalue=element2.options[element2.selectedIndex].value
  	  	} else {
  	  	altvalue=element2.value }
  	  success = (me.value==altvalue)
  	  if (vspec.matchtype) {
  	  	if (vspec.matchtype=='!') {success= 1-success}
  	  	if (vspec.matchtype=='<>') {success= !(altvalue>"")&&( me.value>"")}
  	  	}
	  if (!success) {
		TV_errormsg=TV_errormsg + vmsg		//didn't match; don't pass 
		return false
		}
	  }

  	// are we empty data?
	if (me.value=='') {
		if (vspec.optional) {
//	  alert ("testing optional on '" + fieldname)
			if (vspec.optional==true) {return true}	//that's OK...we're allowed to be blank and we are.
			element2=me.form.elements[vspec.optional]  //only allowedto be blank if other field is too.
		    vmsg=vmsg.replace("{optional}",vspec.optional) //get ready; tell the world the other field name
	  		if (element2.type.substring(0,3)=='sel') {  //get the other field's value
  	  			altvalue=element2.options[element2.selectedIndex].value
 	 	  	} else {
  		  		altvalue=element2.value
  		  	}
//		  alert ('altvalue field is  '+ element2.name)
//		  alert ('altvalue value is  '+ altvalue)

			if (altvalue>'') {
				TV_errormsg=TV_errormsg + vmsg 
				return false //our trigger value isn't blank but we are - bad news
			} else {
				return true
			}
	  	} else { 
			TV_errormsg=TV_errormsg + vmsg		//OPTIONAL flag isn't there; we must be filled in so don't pass 
			return false
		}
	}

    if (!vspec.regex) {return true}			//if no regex exists, we're done....
  	result=vspec.regex.exec(me.value)			//if regex exists, validate it
  	if (result==null) {
		TV_errormsg=TV_errormsg + vmsg			//it didn't pass 
		return false
		}
	
	  //it passed regex at least; reformat it and redisplay it 
  	if (vspec.result) {
		  tmp=vspec.result
		  me.value=eval(tmp.replace(/\{/g,"result[").replace(/\}/g,"]"))
		  }
  	  
  	//now, do we have any min/max testing?
 	 if (vspec.min||vspec.max) {
		//first we extract the whole field or our submatches from our (cleaned-up) datafield
		result= (vspec.result) ? vspec.regex.exec(me.value): result=me.value

		//convert it/them to an actual value -- according to internal model if there is one.
		tval=eval( vspec.internal? vspec.internal.replace(/\{/g,"result[").replace(/\}/g,"]") : me.value)

		//if we could detect a failure here, we could fail validation 
		//if no min or max specified, create something to test that always passes
		tmin = (vspec.min)? eval(vspec.min): tval - 1 
		tmax = (vspec.max)? eval(vspec.max): tval + 1 
		if ((tval>tmax)||(tval<tmin)) {
			TV_errormsg=TV_errormsg + vmsg			//it didn't pass 
			return false
			}
		}
		return true

	}
}



//------------------
//v1.20 2/19/02 LG pass in optional overriding formula or allow default "formulas"
//v1.10 3/21/01 LG formulas can get too long, so FIRST split, THEN parse
//v1.0a 1/24/01 LG
//can we ignore case? can we errorcheck formula(s)? Independantly?

function TV_calcForm(ftext) {
if (ftext==null) {
	clist=document.forms[0].formulas.value.split(";")			//separate out multiple 
} else {
	clist=ftext.split(";")										//separate out multiple 
}
	for (x = 0; x < clist.length; x++) {						//look at each line
		calc=clist[x]
		calc=searchReplace(calc, "==", "||")					//convert boolean equalities temporarily to alternate form (so we can s/r for plain "=" in a moment
		calc=searchReplace(calc, "}=", ".value=")				//convert the assignment operations
		calc=searchReplace(calc, "||", "==")					//convert equalities back
		calc=searchReplace(calc, "{", "parseFloat(document.forms[0].")	//convert the left side of all fieldnames to full javascript format
		//convert the right side of a fieldname to full format; either for text or checkboxes
		while (calc.indexOf("}")>-1) { 
			loc=calc.indexOf("}")
			prev=calc.lastIndexOf(".",loc)
			if (!document.forms[0].elements[calc.substring(prev+1,loc)]) {
				alert ("Formula error: "+calc.substring(prev+1,loc)+" field was not found.")
				return
				}
			if (document.forms[0].elements[calc.substring(prev+1,loc)].type=="checkbox") {
				calc= calc.substring(0,loc) + ".checked*1)" +  calc.substring(loc+1,999)
	 		} else {
				calc= calc.substring(0,loc) + ".value*1)" +  calc.substring(loc+1,999)
				}
			}
			//remove any opening "parseFloat"
		if (calc.substring(0,11)=="parseFloat(") {calc=calc.substring(11,999)}
		if (calc.substring(0,12)==" parseFloat(") {calc=calc.substring(12,999)}
		if (document.forms[0].test) { alert(calc)}
		eval(calc)
		}
	}

	function searchReplace(mystring,searchtext,replacetext) {
	tstring = mystring
	while (tstring.indexOf(searchtext)>-1) { 
		loc=tstring.indexOf(searchtext)
		tstring = tstring.substring(0,loc) + replacetext +  tstring.substring(loc+searchtext.length,999)
		}
	return (tstring)
	}

//------------------
//v1.1  11/6/00 LG - reviewed and added to basic Forms Processing
//	takes two+ variables-- Required: THIS field; number of words needed;
//	optional...number of words across all relevant fields, and a list of additional fields

function TV_countWords(field,numwords) {
	mytext = field.value
	found=0
	wc=0
	while (found>=0) {
		found=mytext.indexOf(" ",found+2)	//look for a space past the next word
		wc++							//add 'em up
		}
	if (wc>(numwords+1)) {				//past the limit?
		dw=wc-numwords
		alert ("You have exceeded the "+numwords+" word limit.\nPlease delete at least "+dw+" words.")
  		field.focus()
		}
	if (TV_countWords.arguments.length > 2) {		//check also if they want a total-word count on X number of fields  
		tw =TV_countWords.arguments[2]
		thisform = field.form
		for (furtherfield=3; furtherfield<TV_countWords.arguments.length; furtherfield++) {
			mytext = thisform[TV_countWords.arguments[furtherfield]].value
			found = 0
			while (found>=0) {
				found=mytext.indexOf(" ",found+1)
				wc++
				}
			}
		if (wc>(tw+1)) {					//exceeded THAT limit!
			dw=wc-tw
			alert ("You have exceeded the "+tw+" total word limit.\nPlease delete at least "+dw+" words from this or another field.")
			field.focus()
			}
		}
	}
	
//associative arrays like our validation araays cannot be 'join'ed -- this allows us to do add array2's fields to array1
// 08/19/03 LG

function TV_joinArray(stuff) {
		argc=arguments
		temparray=new Array()
		for (x=0;x<arguments.length;x++) {
			for (fields in argc[x]) {
				temparray[fields]= argc[x][fields]
				}
			}
		return temparray
	}

