 
/*
*   Oxfam Javascript Validation code.
*   Copyright (C) 2004 Alan Knowles <alan@akbkhome.com>
*
*   This program utilizes a private lookup to a postcode database,
*   Usage of this system is only for the purposes of vistors to the
*   Oxfam family of websites. 
*   Usage for other purposes is strictly prohibited, and will be regarded
*   as Abuse of a private computer system.
*
*   Yes - we know you can read this code, understand what it does, and do your
*   own lookups on our database. but we reserve the right to take legal action 
*   for abuse of private computer systems by doing this - DONT DO IT!!
*
* <script language="javascript"> // added to make the text editors like this file..
*/




/**************************** Class Oxfam_Validate ****************************/

/* Usage:

v = new Oxfam_Validate(form);
v.add('email', 'test1');
v.add('postcode','xxx yyyy');
v.add('empty','xxx','you must fill in XXXX');


form['submit'].value = "Checking";
err = v.validate()
if ("OK" != err) {
    alert(err);
} else {
    form.submit();
}

// get a list of postcodes and address info
v = new Oxfam_Validate(form);
ar = v.loadPostCodes("SG5 4VV")

// fill form from url:
v = new Oxfam_Validate(form);
v.setFromURL();

*/







function Oxfam_Validate(formobj) {
    this.formobj        = formobj;
    
    this._call          = Oxfam_Validate_call;
    this.add            = Oxfam_Validate_add;
    this.validate       = Oxfam_Validate_validate;
    this.xmlhttprequest = Oxfam_Validate_xmlhttprequest;
    this.xmlhttprequestWithCallback = Oxfam_Validate_xmlhttprequestWithCallback;
    
    // validation routines :
    this.checkEmail     = Oxfam_Validate_checkEmail;
    this.checkPostCode  = Oxfam_Validate_checkPostCode;
    
    // utiliity stuff:
    this.loadPostCodesWithCallback =  Oxfam_Validate_loadPostCodesWithCallback;
    this.loadPostCodes  = Oxfam_Validate_loadPostCodes;
    this.setFromURL     = Oxfam_Validate_setFromURL;
    
    this.errors         = new Array();
    this.sendArgs       = new Array();
    
}





    /* add a pair to validate */
    function Oxfam_Validate_add(test, itemname)
    {
        message = '';
        if (arguments.length > 2) {
            message = arguments[2];
        }
        switch (test) {
            case 'emailsyntax':
                var err = this.checkEmail(this.formobj[itemname].value);
                if("OK" != err) {
                    this.errors[this.errors.length] = err;
                }
                break;


            case 'email':
                var err = this.checkEmail(this.formobj[itemname].value);
                if("OK" != err) {
                    this.errors[this.errors.length] = err;
                    break;
                }
                this.focus = itemname;
		//alert(escape(this.formobj[itemname].value));
                this.sendArgs[this.sendArgs.length] = 'email['+itemname+']='+encodeURIComponent(this.formobj[itemname].value);
                break;
                
                
            case 'postcode':
                var err = this.checkPostCode(this.formobj[itemname].value)
                if ("OK" != err) {
                    this.errors[this.errors.length] = err;
                    this.focus = itemname;
                    break;
                }
                this.sendArgs[this.sendArgs.length] = 'postcode['+itemname+']='+encodeURIComponent(this.formobj[itemname].value);
                break;
                
            case 'empty':
                var shortValue = this.formobj[itemname].value.replace(/ /gi,'');
                if (!shortValue.length) {
                    this.errors[this.errors.length] = message ? message : 'You should fill in all required fields';
                    this.focus = itemname;
                }
                break;
            case 'checked':
                //alert(this.formobj[itemname].value);
                
                for(var n=0;n < this.formobj[itemname].length;n++) {
                    
                    //alert(this.formobj[itemname][n]);
                    if (this.formobj[itemname][n].checked == true) {
                        return;
                    }
                    
                }
                this.errors[this.errors.length] = message;
                break;
                 
        }
       
    }
    /* do the validation. */
    function Oxfam_Validate_validate() 
    {
        // loop through the args.. 
         
        // got errors - show them..
        if (this.errors.length) {
            if (this.focus) {
                this.formobj[this.focus].focus();
            }
            return this.errors.join("\n");
        }
        // do call if necessary
        if (this.sendArgs.length) {
            return this._call(this.sendArgs.join("&"));
        }
        return "OK";
    
    
    }
    /* xml httprequest wrapper. */
    
    function Oxfam_Validate_xmlhttprequest(req) 
    {
        
        try {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (ee) {
                try {
                   xmlhttp = new XMLHttpRequest();
                } catch(eee) {
                    return "OK";
                }
            }
        }
        
        if (!xmlhttp) {
            //alert('oops cant do rpc!');
            return "OK";
        }
        
         //document.write(req);
        try {
            xmlhttp.open("GET", req ,false);
        
            xmlhttp.send('');
        } catch (e) {
           // alert('catch error'+e);
            return 'OK';
        }
        switch(xmlhttp.readyState) {
        //return;
            case 1,2,3:
                //alert('Bad Ready State: '+httpRequest.status);
                return 'OK';
                break;
            case 4:
                if(xmlhttp.status !=200) {
                    //alert('bad status');
                    return "OK";
                }
                return xmlhttp;
            
        }
        return 'OK';
    }
    
    
    function Oxfam_Validate_xmlhttprequestWithCallback(req,callback,args) 
    {
        var xmlhttp =  false;
        try {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (ee) {
                try {
                   xmlhttp = new XMLHttpRequest();
                } catch(eee) {
                    return "OK";
                }
            }
        }
        
        if (!xmlhttp) {
            //alert('oops cant do rpc!');
            return "OK";
        }
        
         //document.write(req);
        try {
            xmlhttp.open("GET", req ,true, null, null);
            var _t = this;
            xmlhttp.onreadystatechange = function (aEvt) {
                switch ( xmlhttp.readyState ) {
                    case 1, 2, 3:
                         break;
                          
                    case 4:
                        if ( xmlhttp.status != 200 ) {
                            alert("The server respond with a bad status code: " + xmlhttp.status +"\n" + _t.getFullURL());
                            callback(false,data);
                            return;
                        }
                    
                        if(xmlhttp.status != 200) {
                            alert("Error loading page\n");
                            callback(false,data);
                            return;
                        }
                      
                        callback(xmlhttp,args);
                        return;
                }
                 

            };
        
            xmlhttp.send('');
        } catch (e) {
            alert('catch error'+e);
            return 'ERROR';
        }
        
    }
    
    
    /* call the validation server.  (private) */
     
    
    function Oxfam_Validate_call(args) 
    {
    // todo:: display checking warning.. ???
        //alert('doing call');
        xmlhttp = this.xmlhttprequest("/javascript/validate.php?"+args );
        
        if ((typeof(xmlhttp) == 'string') && (xmlhttp  == 'OK')) {
            return 'OK';
        }
        //alert('got this far!');
        response  = xmlhttp.responseText;
        
        
        if (!response || (response == 'OK')) {
            return "OK";
        }
       // alert('got'+ response);
        return response;
       
        
    }
    
        
    function Oxfam_Validate_checkPostCode(pc) { //check postcode format is valid
        var test = pc; 
        var size = test.length
        test = test.toUpperCase(); //Change to uppercase
        while (test.slice(0,1) == " ") { //Strip leading spaces
            test = test.substr(1,size-1);size = test.length
        }
        while(test.slice(size-1,size) == " ") { //Strip trailing spaces
            test = test.substr(0,size-1);size = test.length
        }
        if (size == 0) {
            return "please enter a valid postcode";
        }
        //document.details.pcode.value = test; //write back to form field
        if (size < 6 || size > 8){ //Code length rule
            return "please enter a valid postcode";
            return test + " is not a valid postcode - wrong length";
        }
        if (!(isNaN(test.charAt(0)))){ //leftmost character must be alpha character rule
            return "please enter a valid postcode";
            return   test + " is not a valid postcode - cannot start with a number";
        }
        if (isNaN(test.charAt(size-3))){ //first character of inward code must be numeric rule
            return "please enter a valid postcode";
            return test + " is not a valid postcode - alpha character in wrong position";
            
        }
        if (!(isNaN(test.charAt(size-2)))){ //second character of inward code must be alpha rule
            return "please enter a valid postcode";
            return test + " is not a valid postcode - number in wrong position";
        }
        if (!(isNaN(test.charAt(size-1)))){ //third character of inward code must be alpha rule
            return "please enter a valid postcode";
            return test + " is not a valid postcode - number in wrong position";
        }
        if (!(test.charAt(size-4) == " ")){//space in position length-3 rule
            return "please enter a valid postcode";
            return test + " is not a valid postcode -   space in wrong position";
            
        }
        count1 = test.indexOf(" ");count2 = test.lastIndexOf(" ");
        if (count1 != count2){//only one space rule
            return "please enter a valid postcode";
            return test + " is not a valid postcode - only one space allowed";
        }
        return "OK";
    }
    
    
    function Oxfam_Validate_checkEmail(emailStr) {

        var emailPat=/^(.+)@(.+)$/
        var specialChars="\\(\\)<>@,;:\\\\\\\"\\.\\[\\]"
        var validChars="\[^\\s" + specialChars + "\]"
        var quotedUser="(\"[^\"]*\")"
        var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/
        var atom=validChars + '+'
        var word="(" + atom + "|" + quotedUser + ")"

        // The following pattern describes the structure of the user
        var userPat=new RegExp("^" + word + "(\\." + word + ")*$")
        var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$")

        var matchArray=emailStr.match(emailPat)

        //browser detection...
        var browser=navigator.appName;
        if (browser.indexOf("Netscape") >= 0) { return "OK"; }

        if (matchArray==null) {
            /* Too many/few @'s or something; basically, this address doesn't
		         even fit the general mould of a valid e-mail address. */
		      var errStr="The email address you typed has an error.\n\n"
		      errStr += "_______________________________________________\n\n";
		      errStr += "A valid email address has only one @ sign, at least one\n";
		      errStr += "dot (.), no spaces and the @ is followed by more text \n";
		      errStr += "_______________________________________________\n\n";
		      errStr += "Please check what you typed and correct the error.\n";
		      return errStr;
        }
            
        var user=matchArray[1]
        var domain=matchArray[2]

        // See if "user" is valid
        if (user.match(userPat)==null) {
		      // user is not valid
		      var errStr="The email address you typed has an error.\n\n"
		      errStr += "_______________________________________________\n\n";
		      errStr += "A valid email address must have some characters \n";
		      errStr += "before and after the @ sign.  \n";
		      errStr += "_______________________________________________\n\n";
		      errStr += "Please check what you typed and correct the error.\n";
		      return errStr;
		      
        }

        /* if the e-mail address is at an IP address (as opposed to a symbolic
	        host name) make sure the IP address is valid. */
        var IPArray=domain.match(ipDomainPat)
        if (IPArray!=null) {
		      // this is an IP address
		      for (var i=1;i<=4;i++) {
			       if (IPArray[i]>255) {
				       var errStr="The email address you typed has an error.\n\n"
				       errStr += "_______________________________________________\n\n";
				       errStr += "A valid email address with numbers after the @ sign\n";
				       errStr += "must be in the form [255.255.255.255] \n";
				       errStr += "_______________________________________________\n\n";
				       errStr += "Please check what you typed and correct the error.\n";
				       return errStr;
			          
			       }
		      }
		      return "OK";
        }

        // Domain is symbolic name
        var domainArray=domain.match(domainPat)
        if (domainArray==null) {
		      var errStr="The email address you typed has an error.\n\n"
		      errStr += "_______________________________________________\n\n";
		      errStr += "A valid email address must have characters \n";
		      errStr += "after the @ sign. Most symbols are not valid, \n";
		      errStr += "including spaces in the middle, beginning or end. \n";
		      errStr += "_______________________________________________\n\n";
		      errStr += "Please check what you typed and correct the error.\n";
		      return errStr;
        }

	     /* domain name seems valid, but now make sure that it ends in a
	        three-letter word (like com, edu, gov), a two-letter word, representing
	        country (uk, nl) or a new four letter domain (info) and that there's a
	        hostname preceding the domain or country. */

	     /* Now we need to break up the domain to get a count of how many atoms
	        it consists of. */
	     var atomPat=new RegExp(atom,"g")
	     var domArr=domain.match(atomPat)
	     var len=domArr.length
	     if (domArr[domArr.length-1].length<2 || domArr[domArr.length-1].length>4) {
	         // the address must end in a two letter or three letter word.
	         var errStr="The email address you typed has an error.\n\n"
	         errStr += "_______________________________________________\n\n";
	         errStr += "The address must end in a two, three or four letter domain,\n";
	         errStr += "or country code like: '.COM', '.INFO' or '.UK'\n";
	         errStr += "_______________________________________________\n\n";
	         errStr += "Please check what you typed and correct the error.\n";
	         return errStr;
	     }

        // Make sure there's a host name preceding the domain.
	     if (len<2) {
	         var errStr="The email address you typed has an error.\n\n"
	         errStr += "_______________________________________________\n\n";
	         errStr += "A valid email address has at least three parts separated \n";
	         errStr += "by the @ symbol and dots like: yourname@yourhost.com \n";
	         errStr += "_______________________________________________\n\n";
	         errStr += "Please check what you typed and correct the error.\n";
	         return errStr;
	     }

	     // If you've gotten this far, everything's valid!
	     return "OK";
	}
    
    
    function  Oxfam_Validate_loadPostCodesWithCallback(postcode,callback) 
    {
        var data = new Array(postcode,callback);
        xmlhttp = this.xmlhttprequestWithCallback(
            "/javascript/postcodelookup.php?code=" + encodeURIComponent(postcode), 
            Oxfam_Validate_loadPostCodesCallback,
            data);
    }
    
    function  Oxfam_Validate_loadPostCodesCallback(xmlhttp,data) 
    {
        var postcode = data[0]
        var callback = data[1]
    
    
        if ((typeof(xmlhttp) == 'string') && (xmlhttp  == 'OK')) {
            callback('OK');
        }
        //alert('got this far!');
        //setPostCodeError(err,'look up failed');
        //return;
        var base = null;
        var response  = xmlhttp.responseXML;
        if (!response || !response.childNodes) {
            callback(postcode + ' is not a valid postcode - ' + xmlhttp.responseText);
            return;
        }
        if (response.childNodes[0] && response.childNodes[0].nodeName == 'addresses')  {
            base = response.childNodes[0].childNodes;
        }
        if (response.childNodes[1] && response.childNodes[1].nodeName == 'addresses')  {
            base = response.childNodes[1].childNodes;
        }
        if (!base) {
            callback(postcode + ' is not a valid postcode - ' + xmlhttp.responseText);
            return;
        }
        //alert(response.childNodes[0].childNodes.length);
        var ret = new Array();
        ret['addresses'] = new Array();
        
        var i,r=0;
         
        for (i=0;i<base.length;i++) {
            if (!base[i].attributes) {
                continue;
            }
            if (base[i].nodeName == 'address')  {
                /* 
                listbox.length = listbox.length + 1;
                listbox.options[listbox.length-1].text =   base[i].childNodes[0].nodeValue;
                listbox.options[listbox.length-1].value  =   (base[i].attributes['id']) ? 
                    base[i].attributes['id'].value : base[i].attributes[0].nodeValue;
               */
               
                ret['addresses'][ret['addresses'].length] =   base[i].childNodes[0].nodeValue;
                continue;
                
            }
            if (!base[i].childNodes.length) {
                continue;
            }
            ret[base[i].nodeName] =  base[i].childNodes[0].nodeValue;
            
            
            //alert("got " + response);
        }
        callback(ret);
        //alert(ret['addresses'][1]);
        return;
    
    
    
    
    
    
    }
    function  Oxfam_Validate_loadPostCodes(postcode) 
    {
         //alert('doing call');
        xmlhttp = this.xmlhttprequest("/javascript/postcodelookup.php?code="+encodeURIComponent(postcode));
      
         if ((typeof(xmlhttp) == 'string') && (xmlhttp  == 'OK')) {
            return 'OK';
        }
        //alert('got this far!');
        //setPostCodeError(err,'look up failed');
        //return;
        var base = null;
        var response  = xmlhttp.responseXML;
        if (!response || !response.childNodes) {
            return  postcode + ' is not a valid postcode - ' + xmlhttp.responseText;
        }
        if (response.childNodes[0] && response.childNodes[0].nodeName == 'addresses')  {
            base = response.childNodes[0].childNodes;
        }
        if (response.childNodes[1] && response.childNodes[1].nodeName == 'addresses')  {
            base = response.childNodes[1].childNodes;
        }
        if (!base) {
            return postcode + ' is not a valid postcode - ' + xmlhttp.responseText;
        }
        //alert(response.childNodes[0].childNodes.length);
        var ret = new Array();
        ret['addresses'] = new Array();
        
        var i,r=0;
         
        for (i=0;i<base.length;i++) {
            if (!base[i].attributes) {
                continue;
            }
            if (base[i].nodeName == 'address')  {
                /* 
                listbox.length = listbox.length + 1;
                listbox.options[listbox.length-1].text =   base[i].childNodes[0].nodeValue;
                listbox.options[listbox.length-1].value  =   (base[i].attributes['id']) ? 
                    base[i].attributes['id'].value : base[i].attributes[0].nodeValue;
               */
               
                ret['addresses'][ret['addresses'].length] =   base[i].childNodes[0].nodeValue;
                continue;
                
            }
            if (!base[i].childNodes.length) {
                continue;
            }
            ret[base[i].nodeName] =  base[i].childNodes[0].nodeValue;
            
            
            //alert("got " + response);
        }
        //alert(ret['addresses'][1]);
        return ret;
     
    
    //alert(postcode);
    
    }

    /**
    * Auto fill a form based on the _GET arguments.
    *
    */
  
    function Oxfam_Validate_setFromURL() 
    {
    
        if (!this.formobj) {
            return;
        }
        if (location.href.search(/\?/gi) == -1) {
            return;
        }
        shortRequest = location.href.substr(location.href.search(/\?/gi)+1);
        //alert("cRq "+cRq);
        var shortRequestArray = shortRequest.split("&");
        //alert("aRq "+aRq);
        for (var ia=0;ia<shortRequestArray.length;ia++) {
            var keyValue = shortRequestArray[ia];
            //alert("Element "+ia+": "+c);
            if (keyValue.search(/\=/gi) != -1) {
                var key    = unescape(keyValue.substring(0,keyValue.search(/\=/gi)));
                var value  = unescape(keyValue.substr(keyValue.search(/\=/gi)+1)).replace(/\+/g, ' ');
                //alert("ID/Val "+cID+": "+cVal);
                
                if (!this.formobj[key]) {
                    continue;
                }
                switch (this.formobj[key].tagName) {
                    case "INPUT":
                        
                        if (this.formobj[key].type == 'checkbox') {
                            this.formobj[key].checked = true;
                            break;
                        }
                        this.formobj[key].value = value;
                        break;
                    case "SELECT":
                        for(var i =0; i<this.formobj[key].options.length;i++) {
                            if (this.formobj[key].options[i].value == value) {
                                this.formobj[key].selectedIndex = this.formobj[key].options[i].index;
                                break;
                            }
                        }
                                
                        break; // not supported?
                    default:
                        break;
                }
            }
        }
    }

