//Original Author: Ben Przystanski - ben@graphico.co.uk
//Released under GNU General Public License : http://www.gnu.org/copyleft/gpl.html
//

//=======================================================================
// START VALIDATION FUNCTIONS
//=======================================================================

//basic validation function - can be overridden by local version in calling page if required
function validateForm(frm)
{
vfrm = new vForm(frm);
return vfrm.validate();
}

function debugForm(frm)
{
vfrm = new vForm(frm);
vfrm.info();
return false;
}

//support functions
function isEmpty(str)
{
var empty=true;
if (str.length!= 0) {
for (var i=0;i<str.length;i++)
{
if (str.charAt(i) != " ") 
{
empty = false; 
break;
}
}
}
return empty;
}

function inArray(arr,str)
{
for (var i=0;i<arr.length;i++)
{
if (arr[i]==str) return true;
}
return false;
}

function isRadioChecked(rad)
{
for (var i=0;i<rad.length;i++)
{
if (rad[i].checked) return true;
}
return false;
}

function iif(a,b,c){if(a){return b;}else{return c;}}

//sub-validation functions

function isNumber(str){
var r1 = new RegExp("[a-z]|[A-Z]");
return (!r1.test(str));
}

function isTelephone(str) {
// are regular expressions supported?
var supported = 0;
if (window.RegExp) {
var tempStr = "a";
var tempReg = new RegExp(tempStr);
if (tempReg.test(tempStr)) supported = 1;
}
if (!supported)
return (true);
var r1 = new RegExp("[0-9]");
return (r1.test(str));
}

function isEmail(str) {
var supported = 0;
if (window.RegExp) {
var tempStr = "a";
var tempReg = new RegExp(tempStr);
if (tempReg.test(tempStr)) supported = 1;
}
if (!supported) 
return (str.indexOf(".") > 2) && (str.indexOf("@") > 0);
var r1 = new RegExp("(@.*@)|(\\.\\.)|(@\\.)|(^\\.)");
var r2 = new RegExp("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$");
return (!r1.test(str) && r2.test(str));
}

function isImage(str,atypes)
{
var pos = str.lastIndexOf('.');
var ext = str.substring(pos+1).toLowerCase();
if (atypes==null) {
atypes = new Array('gif','jpg','jpeg');
}
for (var i=0;i<atypes.length;i++)
{
if (ext==atypes[i]) {
return true;
}
}
return false;
}

function getscope(s)
{
if (s=='all') return vAll;
if (s=='text') return vText;
return vNone;
}

function getsubtype(fld)
{
if (fld.vtype) return fld.vtype;
if (inArray(aFormFocusTypes,fld.type)) return 'txt';
return fld.type;
}

//enums

var vDefault=0;

var vNatural=0;
var vFirst=1;
var vLast=2;

var vRequired = 0;
var vLength = 1;
var vError = 2;
var vOther = 3;

var vNone=0;
var vAll=1;
var vText=2;

//arrays

var aValTypes = new Array("txt","eml","num","tel","img","match");

var aFormExcludeTypes = new Array("hidden","button","submit","reset","image");
var aFormFocusTypes = new Array("text","password","textarea");
var aFormTextTypes = new Array("text","textarea");
var aFormTypes = new Array("text","password","textarea","file","radio","checkbox","select-one","select-multiple");

//validation objects/methods

function vForm(obj) //object
{
var x=new Array(obj.length);
var hasfile=false;
var vflength=0;
this.hasfile=false;
for (var f=0;f<obj.length;f++){
x[f]=new vField(obj,f);
if (!inArray(aFormExcludeTypes,x[f].type)) vflength++;
if (x[f].type=='file') this.hasfile=true;
x[x[f].name]=x[f];
}
this.Fields = x;
this.Fields.length = x.length;
this.vlength = vflength;
this.focusorder = vNatural;
this.separator = "-";
this.header = "";
this.footer ="";
this.title = iif((!obj.vtitle),"Form Validation:",obj.vtitle);
this.subtitle = new Array(3);
this.scope=iif((!obj.vscope),vNone,getscope(obj.vscope));
this.obj = obj;
this.name = obj.name;
this.required = (obj.vreq=='');
this.method = obj.method;
this.action = obj.action;
this.enctype = obj.enctype;
this.error=false;
this.checkerror(true);

}

function vField(form,fieldnum) //object
{
var obj=form[fieldnum];

this.name = obj.name; 
this.type = obj.type;
this.vtype = obj.vtype;
this.target = iif((!obj.vtarget),'',obj.vtarget);
this.validate = (!inArray(aFormExcludeTypes,obj.type));
this.specified = iif((obj.von==''),true,null);
this.specified = iif((obj.voff==''),false,this.specified);
this.min = parseInt(obj.vmin);
this.max = parseInt(obj.vmax);
this.desc = obj.vdesc;
this.maxlength = parseInt(obj.maxlength);
this.required = iif((obj.vreq==''),true,null);
this.optional = iif((obj.voptional==''),true,false);
this.checked = iif((!obj.checked),false,true);
this.value = obj.value;
this.obj = obj;
this.supported = null; //reserved
this.valid = false;

}

function vFormError(silent) //method
{

var title="vForm() Error:\n";
var msg="";

if (this.hasfile&&(this.method!="post"||this.enctype!="multipart/form-data")) {
this.error=true;
msg+="\nForm contains input type='file', but the method/enctype attributes are not set correctly.";
}

for (var f=0;f<this.Fields.length;f++)
{ 
var fld=this.Fields[f];
var desc = iif(fld.desc,fld.desc,fld.name);
if ((!fld.excluded)&&(fld.vtype&&!inArray(aValTypes,fld.vtype))) {
msg+="\nField '" + desc + "' has an invalid vtype attribute value: '"+fld.vtype+"'";
}
}

if (msg) {
var ret = title+msg;
this.error = true;
if (!silent) {
alert(ret);
} else {
return ret;
}
}

}
vForm.prototype.checkerror = vFormError;

function vFormInfo() //method
{

this.checkerror();
if (this.error) return false;

var title="vForm() Info:\n";
var msg="";

msg+="\n"+this.vlength+" of "+this.Fields.length+" fields will be validated\n";

for (var f=0;f<this.Fields.length;f++)
{ 
var fld=this.Fields[f];
if (fld.type!="none") {
msg+="\nField: "+fld.name;
if (fld.required) msg+="*";
msg+=" ["+fld.type+"]: ";
if (fld.maxlength) msg+="maxlength="+fld.maxlength;
if (fld.min) msg+=" min="+fld.min;
if (fld.max) msg+=" max="+fld.max;
if (fld.desc) {
msg+=" desc='"+fld.desc+"'";
} else {
msg+=" NO DESC";
}
}
}

alert(title+msg+"");

}
vForm.prototype.info = vFormInfo;

function vFormValidate() //method
{
this.checkerror();
if (this.error) return false;

var title=this.title;
var eMsg=new Array('','','','');
var eField=new Array('','','','');
var bMsg,msg,firstError;
var aRadio=new Array();

for (var f=0;f<this.Fields.length;f++)
{ 
var fld=this.Fields[f];

var desc = iif(fld.desc,fld.desc,fld.name);
if(this.separator!="") desc = this.separator + " " + desc;

var thisError = false;

var include=false;
var validate=false;
var required=this.required;

//define checking scope
include=!inArray(aFormExcludeTypes,fld.type);
if (include) {
if (this.scope==vText&&inArray(aFormTextTypes,fld.type)&&fld.specified!=false) {
validate=true;
}
else if(this.scope==vNone&&fld.specified) {
validate=true;
}
else if(this.scope==vAll&&fld.specified!=false) {
validate=true;
}
if (fld.required==false||fld.optional==true) required=false;
if (fld.required==true) required=true;
} else
{
required=false;
}
if (!validate) required=false;//for clarity

//check required
if (required&&isEmpty(fld.value)) {
eMsg[0]+="\n"+desc+" is required";
if (!eField[0]&&inArray(aFormFocusTypes,fld.type)) eField[0] = fld.name;
} else

//check length
if (validate&&fld.value!=""||fld.type=='select-one'||fld.type=='select-multiple') {
if (fld.value.length>fld.maxlength) {
var over = fld.value.length-fld.maxlength;
eMsg[1]+="\n"+desc+" is "+over+" character"+iif(over>1,'s','')+" too long";
if (!eField[1]&&!inArray(aFormFocusTypes,fld.type)) eField[1] = fld.name;
} else

//check text validation type errors / form element selection requirements
if (validate) {
var subtype = getsubtype(fld);
switch (subtype) {
case 'eml': 
if (!isEmail(fld.value)) {
eMsg[2]+="\n"+desc+" should be a valid email address";
if (!eField[2]) eField[2] = fld.name;
}
break;
case 'num': 
if (!isNumber(fld.value)) {
eMsg[2]+="\n"+desc+" should be a number";
if (!eField[2]) eField[2] = fld.name;
} else {
if (fld.min||fld.max){
var minout = parseInt(fld.value)<fld.min;
var maxout = parseInt(fld.value)>fld.max;
if (minout||maxout) {
if (fld.min&&fld.max) {
eMsg[2]+="\n"+desc+" should be within the range "+fld.min+" to "+fld.max;
} else if (fld.min) {
eMsg[2]+="\n"+desc+" should be "+fld.min+" or more";
} else if (fld.max) {
eMsg[2]+="\n"+desc+" should be "+fld.max+" or less";
}
if (!eField[2]) eField[2] = fld.name;
}
}
}
break;
case 'tel':
if (!isNumber(fld.value)) {
eMsg[2]+="\n"+desc+" should be a number";
if (!eField[2]) eField[2] = fld.name;
} else {
if (!isTelephone(fld.value)) {
eMsg[2]+="\n"+desc+" should be a valid telephone number";
if (!eField[2]) eField[2] = fld.name;
}
}
break;
case 'img': 
if (!isImage(fld.value)) {
eMsg[2]+="\n"+desc+" should be an image file";
}
break;
case 'radio':
if (!inArray(aRadio,fld.name)) {
aRadio[aRadio.length+1]=fld.name;
var rad = this.obj[fld.name];
if (!isRadioChecked(rad)) {
if (required) eMsg[2]+="\n"+desc+" should have an option selected";
}
}
break;
case 'select-one': 
var sel = this.obj[fld.name];
if (sel.selectedIndex==-1||sel[sel.selectedIndex].value==""||sel[sel.selectedIndex].value=="-") {
if (required) eMsg[2]+="\n"+desc+" should have an item selected";
}
break;
case 'select-multiple': 
var sel = this.obj[fld.name];
if (sel.selectedIndex==-1) {
if (required) eMsg[2]+="\n"+desc+" should have an item selected";
}
break;
case 'checkbox': 
var chk = this.obj[fld.name];
if (!chk.checked&&fld.required) {
if (required) eMsg[2]+="\n"+desc+" must be checked";
}
break;
case 'match':
if (fld.value!=this.obj[fld.target].value) {
if (required) eMsg[2]+="\n"+desc+" must match " + this.Fields[fld.target].desc;
}
break;
case 'txt':
break;
default:
alert("vForm.validate() Error: Unknown subtype: " + subtype);
break;
}
}
}
}



for (var x=0;x<3;x++) {
if (eMsg[x]!="") {
eMsg[x]+="\n";
bMsg = true;
if (!firstError) firstError = eField[x];
}
}

if (bMsg) {

msg = title+"\n";
if (this.header) msg+="\n"+this.header+"\n";

for (var x=0;x<3;x++)
{
if (this.subtitle[x]&&eMsg[x]!="") msg+="\n"+this.subtitle[x];
msg+=eMsg[x];
}

if (this.footer) msg+="\n"+this.footer;

alert(msg);
//if (firstError) eval("this.obj."+firstError+".focus();");
return false;
} else {
return true;
}

}
vForm.prototype.validate = vFormValidate;

//=======================================================================
// END VALIDATION FUNCTIONS
//=======================================================================
