/* $Revision: 529 $ | $CreationDate: 2007-09-18 14:51:15 +0100 (Tue, 18 Sep 2007) $ | $LastChangedDate: 2010-04-26 14:51:14 +0000 (Mon, 26 Apr 2010) $ | $LastChangedBy: andrewgillard $ */

var CBSValidator = {
    /*
	formSubmitHandler : function(ev)
	_testAllFormFields : function(form, onsubmit, onload)
	_showError : function(field, msg, erranchorelement)
	_hideError : function(field)
	_hasErrorDisplayed : function(field)
	_testField : function(field, onsubmit, onload)
	_handleDelayedResult : function(field, result, validatorobj)
	_changeFunc : function(ev)
	_addEvent : function(field, useclickinsteadofchange)
	_addGenericValidator : function(element, testfunction, errormessage, runonsubmit, radiobuttongroupname, erranchorelement)
	addRequiredValidator : function(obj, msg, erranchorelement)
	addNumericValidator : function(obj, msg, erranchorelement)
	addRangeValidator : function(obj, msg, min, max, erranchorelement)
	addNumericIntegerValidator : function(obj, msg, erranchorelement)
	addRangeIntegerValidator : function(obj, msg, min, max, erranchorelement)
	addLengthValidator : function(obj, msg, min, max, erranchorelement)
	addMatchValidator : function(obj, msg, ref, erranchorelement)
	addRegexValidator : function(obj, msg, pattern, erranchorelement)
	addAjaxValidator : function(obj, msg, url, timeout, erranchorelement)
	addTelephoneNumberValidator : function(obj, msg, erranchorelement)
	addCustomValidator : function(obj, msg, testfunc, erranchorelement)
	*/


	/**
	 * Function to call on error-free.
	 *
	 */
	fnToCallOnErrorFree : null,

	/**
	 * Handles the submission of a validated form by checking the validity of
	 *  every field of it, and halting the submission of the form if any of them
	 *  fail
	 *
	 * @param event Event object
	 */
	formSubmitHandler : function(ev) {
	    var form = CBS.getEventTarget(ev);

		if (!CBSValidator._testAllFormFields(form, true)) {
		    //At least one field has an error
			if (!ValidationFormSubmitFailureMessage)
				ValidationFormSubmitFailureMessage = "There are errors in the form you submitted. Please correct these and try submitting it again.";

				//Set the value as 1 for error if on checkout page.
				if(CBS.getEl('checkouterror')){
					CBS.getEl('checkouterror').value=1;
				}else{
				    CBSGUI.showAlert(ValidationFormSubmitFailureMessage);
				}
		} else {
		    //Submit the form if not on checkout page and not intended to call a javascript function.
			if (!CBS.getEl('checkouterror') && !CBSValidator.fnToCallOnErrorFree) return true;

			//If intended to call a javascript function.
		    if (CBSValidator.fnToCallOnErrorFree) {
			    CBSValidator.fnToCallOnErrorFree();

			    //Reset the variable.
			    CBSValidator.fnToCallOnErrorFree = null;
			}
		}

		//Don't submit the form if there is an error or on checkout page or intended to call a
		//javascript function.
		try {
		    //W3C
			ev.preventDefault();
		} catch (ex) {
		    //IE
			return false;
		}
	},

	/**
	 * Tests the validity of all form fields for 'form', returning true if they
	 *  all passed, and false otherwise. Specify onsubmit=true if this function
	 *  is being called because a form is being submitted, and/or onload=true if
	 *  it's being called because the page has just finished loading
	 *
	 * @param element The form to check the fields' validity of
	 * @param boolean Whether this is being called as a form submits
	 * @param boolean Whether this is being called as the page loads
	 */
	_testAllFormFields : function(form, onsubmit, onload) {
		//Give onsubmit a default value of true, because Ajax validators fail
		// horribly if run on submit, so better be safe

		if (onsubmit == undefined) var onsubmit = true;

		//Default onload to false to ensure we don't accidentally skip over
		// unchanged fields
		if (onload == undefined) var onload = false;

		var errorFree = true;
		var fields=CBS.getAllFormFields(form)

		for (var fields=CBS.getAllFormFields(form),i=0,l=fields.length; i<l; i++) {
		    if (!fields[i]) continue;
			if (!CBSValidator._testField(fields[i], onsubmit, onload)) errorFree = false;
		}
		return errorFree;
	},

	/**
	 * Shows an error message for 'field' containing the message 'msg', and adds
	 *  the invalid field CSS class to 'field's current class. If
	 *  'erranchorelement' is specified, the error message is displayed next to
	 *  that element instead, and that element has the invalid field CSS class
	 *  appended to it instead of the field itself
	 *
	 * @param element The element to display the error for
	 * @param string The error message to display
	 * @param element An optional element to display the error message next to instead of the main element
	 */
	_showError : function(field, msg, erranchorelement) {
		CBSValidator._hideError(field);

		if (!erranchorelement) erranchorelement = field;
		if (typeof erranchorelement == 'string') erranchorelement = CBS.getEl(erranchorelement);
		if (!field.CBSValidatorErrorClassObjects) field.CBSValidatorErrorClassObjects = [];

		//Set up some defaults, in case the skin's config.js isn't loaded
		if (!ValidationFailFieldClassName) ValidationFailFieldClassName = 'invalidfield';
		if (!ValidationFailMessageElementType) ValidationFailMessageElementType = 'span';
		if (!ValidationFailMessageAttributes) ValidationFailMessageAttributes = {'style':'color:#a00;'};

		//Add the validation failure class name to the list
		field.className = CBS.addClass(field.className, ValidationFailFieldClassName);
		field.CBSValidatorErrorClassObjects.push(field);
		if (field != erranchorelement) {
			erranchorelement.className = CBS.addClass(erranchorelement.className, ValidationFailFieldClassName);
			field.CBSValidatorErrorClassObjects.push(erranchorelement);
		}
		//Show the error message
		var p = CBS.cE(ValidationFailMessageElementType);
			p.appendChild(CBS.cTN(msg));
			if (ValidationFailMessageAttributes) {
				//Set whatever attributes we want on it
				for (var attr in ValidationFailMessageAttributes) {
					var value = ValidationFailMessageAttributes[attr];
					if (attr == "class" && document.all) {
						//IE wants "className" instead of "class".  Why?  Who knows.
						attr = "className";
					}
					p.setAttribute(attr, value);
				}
			}
			field.CBSValidationErrorObject = p;
			//Then insert it immediately after our INSERT element
			CBS.insertAfter(erranchorelement.parentNode, p, erranchorelement);
	},

	/**
	 * Hides any errors that may exist for 'field'
	 *
	 * @param element The element to hide the errors of
	 */
	_hideError : function(field) {
		if (CBSValidator._hasErrorDisplayed(field)) {
			field.CBSValidationErrorObject.parentNode.removeChild(field.CBSValidationErrorObject);

			//Set up some defaults, in case the skin's config.js isn't loaded
			if (!ValidationFailFieldClassName) ValidationFailFieldClassName = 'invalidfield';
			for (var i=0,l=field.CBSValidatorErrorClassObjects.length; i<l; i++) {
				field.CBSValidatorErrorClassObjects[i].className = CBS.removeClass(field.CBSValidatorErrorClassObjects[i].className, ValidationFailFieldClassName);
			}

			field.CBSValidationErrorObject = null;
		}
	},

	/**
	 * Returns a boolean indicating if the 'field' currently has an error
	 *  displayed
	 *
	 * @param element The element to check the existence of an error of
	 * @return boolean
	 */
	_hasErrorDisplayed : function(field) {
		if (field.CBSValidationErrorObject) return true;
		return false;
	},

	/**
	 * Runs all of the validators for the specified element. If 'onload' is true
	 *  and the field's value hasn't changed from its default, the validators
	 *  will not be run, and if 'onsubmit' is true, only validators that say
	 *  they're able to run on form submission will be run. Returns TRUE if all
	 *  validators passed, FALSE otherwise.
	 *
	 * @param element Field to check validity of
	 * @param boolean Whether this is being called on the submission of a form
	 * @param boolean Whether this is being called as the page finishes loading
	 * @return boolean
	 */
	_testField : function(field, onsubmit, onload) {
	    var value = CBS.getFieldValue(field);

		if (field.tagName.toLowerCase()=='input' && (field.type == 'radio' || field.type == 'checkbox')) {
			//Radio button selection
			if(field.CBSValidatorWatchObject == undefined) {
				// No watch object has been setup, so no constraints have been
				// set on this field
				return true;
			}
			field = field.CBSValidatorWatchObject;
			value = CBS.getSelectedRadioButtonValue(field.name);
		}

		if (field.CBSValidators) {
			//Give onsubmit a default value of true, because Ajax validators
			// fail horribly if run on submit, so better be safe
			if (onsubmit == undefined) var onsubmit = true;

			//Default onload to false so that we don't accidentally skip over
			// unchanged fields
			if (onload == undefined) var onload = false;

			CBSValidator._hideError(field);

			if (onload) {
				if (value == CBS.getFieldDefaultValue(field)) {
					//Running this test on page load, and the field hasn't
					// changed from its default value, so avoid testing it and
					// pretend it's error-free, regardless of its actual error
					// state
					return true;
				}
			}

			if (!field) return true; //Field doesn't exist...?
			if (field.disabled) return true; //Disabled field, so we don't check it
			if (!field.parentNode) return true; //This field has been removed from the document

			for (var i=0,l=field.CBSValidators.length; i<l; i++) {
				if (onsubmit && !field.CBSValidators[i].runonsubmit) continue;
				var testResult = field.CBSValidators[i].testfunc(value, field, onsubmit, onload, field.CBSValidators[i]);
				if (testResult === null) {
					//No result yet, so we wait (probably Ajax)
				} else if (testResult === false) {
					CBSValidator._showError(field, field.CBSValidators[i].errmsg, field.CBSValidators[i].erranchorelement);
					return false;
				}
			}
		}
		return true;
	},

	/**
	 * Should be called by delayed validators (e.g. Ajax) once they know their
	 *  result
	 *
	 * @param element Field whose value was checked
	 * @param boolean Whether the field's value is valid
	 * @param object The object containing details of the validator
	 */
	_handleDelayedResult : function(field, result, validatorobj) {
		if (!CBSValidator._hasErrorDisplayed(field) && !result) {
			CBSValidator._showError(field, validatorobj.errmsg, validatorobj.erranchorelement);
		}
	},

	/**
	 * The function called by a field's onchange event, and calls the test
	 *  function for a field
	 *
	 * @param event The change event object
	 */
	_changeFunc : function(ev) {
		var el = CBS.getEventTarget(ev);
		CBSValidator._testField(el, false, false);
	},

	/**
	 * Adds the change event to 'field' (click instead if
	 *  'useclickinsteadofchange' is true
	 *
	 * @param element The field to attach the event to
	 * @param boolean Whether to use onclick instead of onchange
	 */
	_addEvent : function(field, useclickinsteadofchange) {
		if (!field.CBSValidatorEventAdded) {
			if (useclickinsteadofchange == undefined) useclickinsteadofchange = false;
			CBS.addEvent(field, (useclickinsteadofchange?'click':'change'), CBSValidator._changeFunc);
			field.CBSValidatorEventAdded = true;
		}
	},

	/**
	 * Adds a generic validator with test function 'testfunc' to the element
	 *  specified by 'element'. If the validation check fails, 'errormessage'
	 *  will be displayed (next to the 'element', unless 'erranchorelement' is
	 *  specified, in which case the error message is displayed next to
	 *  'erranchorelement'. The 'element' (and 'erranchorelement') will also
	 *  have the invalid field CSS class appended to their current class list if
	 *  the element's value doesn't match the test function. If 'runonsubmit' is
	 *  true, the validation check will also be run as the form is submitted,
	 *  and will prevent the form submitting if the test function resturns
	 *  false. If 'radiobuttongroupname' is set, the validator will apply to the
	 *  entire radio button group with that name; in which case the
	 *  'erranchorelement' is recommended to be used.
	 *
	 * @param string|element The element to check the validity of
	 * @param function the function(value,element,isonsubmit,isonload,validatorobject) that needs to return a boolean indicating validity
	 * @param string The message to display if the 'element' doesn't pass validation
	 * @param boolean Whether to run this validator on form submission
	 * @param string The name of the radio button group to attach this validator to, if any
	 * @param string|element The element to display the error message to, if not the element itself
	 */
	_addGenericValidator : function(element, testfunction, errormessage, runonsubmit, radiobuttongroupname, erranchorelement) {
		if (typeof element == 'string') element = CBS.getEl(element);
		if (!element.CBSValidators) element.CBSValidators = [];
		//Give onsubmit a default value of false, because Ajax validators fail
		// horribly if run on submit, so better be safe
		if (runonsubmit == undefined) var runonsubmit = false;

		element.CBSValidators.push({'errmsg':errormessage,'testfunc':testfunction,'runonsubmit':runonsubmit,'radiobuttongroupname':radiobuttongroupname,'erranchorelement':erranchorelement});
		if (radiobuttongroupname) {
			var btns = CBS.getRadioButtonsInGroup(radiobuttongroupname);
			for (var i=0, btnsl=btns.length; i<btnsl; i++) {
				//We use onclick events, here, because IE (6 and 7) don't
				// fire onchange events for radios until they lose focus
				CBSValidator._addEvent(btns[i], true);
				btns[i].CBSValidatorWatchObject = element;
			}
		} else {
			CBSValidator._addEvent(element, false);
		}

		var form = CBS.getParentForm(element);
		if (!form.AddedEvents) {
			CBS.addEvent(window, 'load', function(){CBSValidator._testAllFormFields(form, false, true);});
			CBS.addEvent(form, 'submit', CBSValidator.formSubmitHandler);
			form.AddedEvents = true;
		}
	},

    /**
     * A required validator.
     * Requires that the value exists (is >0 chars long).
     * For a radio button, this validator requires that one button in the
     *  group with that button's name="" is selected (but still requires
     *  a specific element's ID or element object be passed as the first
     *  parameter - the rest of the buttons in the group will be determined
     *  from that element).
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addRequiredValidator : function(obj, msg, erranchorelement) {
        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testRequired(null, field);
        }

        if (typeof obj == 'string') obj = CBS.getEl(obj);
        if (obj.tagName.toLowerCase()=='input' && (obj.type == 'radio' || obj.type == 'checkbox')) {
            CBSValidator._addGenericValidator(obj, tf, msg, true, obj.name, erranchorelement);
        } else {
            CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
        }
    },

    /**
     * A numeric validator.
     * Requires that the value is a number.
     * Uses a regular expression because isNaN ignores whitespace
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addNumericValidator : function(obj, msg, erranchorelement) {
        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testNumeric(value);
        }
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
    },

    /**
     * A range validator.
     * Requires that the value is between "min" and "max"
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param float Minimum value
     * @param float Maximum value
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addRangeValidator : function(obj, msg, min, max, erranchorelement) {
        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testRange(value, min, max);
        }
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
    },

    /**
     * A numeric integer validator.
     * Requires that the value is a number (specifically an integer).
     * Uses a regular expression because isNaN ignores whitespace
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addNumericIntegerValidator : function(obj, msg, erranchorelement) {
        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testNumericInteger(value);
        }
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
    },

    /**
     * A range validator.
     * Requires that the value is between "min" and "max" (and an integer)
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param integer Minimum value
     * @param integer Maximum value
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addRangeIntegerValidator : function(obj, msg, min, max, erranchorelement) {
        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testRangeInteger(value, min, max);
        }
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
    },

    /**
     * A length validator.
     * Requires that the value's length is between "min" and "max"
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param integer Minimum length
     * @param integer Maximum length
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addLengthValidator : function(obj, msg, min, max, erranchorelement) {
        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testLength(value, min, max);
        }
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
    },

    /**
     * A match validator.
     * Requires that the value matches that of the 'ref' control
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param string|element Element that this element's value must match
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addMatchValidator : function(obj, msg, ref, erranchorelement) {
        //Ref can be an ID or an object
        if (typeof ref == 'string')
            ref = CBS.getEl(ref);
        if (typeof obj == 'string')
            obj= CBS.getEl(obj);

        var tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testMatch(value, CBS.getFieldValue(ref));
        }
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);

        //Add a change event to the reference element to ensure that changing
        // the reference also affects validation on the main object
        CBS.addEvent(ref, 'change', function(){CBSValidator._testField(obj, false, false);});
    },

    /**
     * A regular expression validator.
     * Requires that the value matches the given "pattern"
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param regex Pattern to match against
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addRegexValidator : function(obj, msg, pattern, erranchorelement) {
        tf = function(value, field, onsubmit, onload) {
            return CBSValidator.testRegex(value, pattern);
        };
        CBSValidator._addGenericValidator(obj, tf, msg, true, null, erranchorelement);
    },

    /**
     * An Ajax validator.
     * Submits an Ajax request to the server, with the value as a POST
     *  value, and expects either TRUE or FALSE back, indicating validity.
     * This validator won't fire when the form is submitted, as it would
     *  lead to an unresponsive application and is difficult to handle.
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param string URL to submit the Ajax request to
     * @param integer Timeout in miliseconds (max: 60000)
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addAjaxValidator : function(obj, msg, url, timeout, erranchorelement) {
        if (timeout == undefined || typeof timeout != 'number' || timeout > 60000) timeout = 5000;
        var tf = function(value, field, onsubmit, onload, validatorobj) {
            var timer;
            var tf2 = function(resp) {
                window.clearTimeout(timer);
                var result = true;
                try {
                    if (resp) {
                        var valid = resp.data;
                        valid = valid.toUpperCase();
                        if (valid != "TRUE") {
                            result = false;
                        }
                    }
                } catch (e) {
                    console.log(e);
                }
                CBSValidator._handleDelayedResult(field, result, validatorobj);
            }
            timerExpire = function() {
                CBSValidator._handleDelayedResult(field, true, validatorobj);
            }
            timer = window.setTimeout(timerExpire, timeout)
            try {
                if (CBSAjax.newRequest(url, tf2, false, 'POST', {'value':value}, false, false)) {
                    return null;
                } else {
                    //If the Ajax request failed to initialise, we'll have to report that the
                    // field is valid, or the form may never be submittable...
                    window.clearTimeout(timer);
                    return true;
                }
            } catch (e) {
                window.clearTimeout(timer);
                console.log(e);
            }
        }
        CBSValidator._addGenericValidator(obj, tf, msg, false, null, erranchorelement);
    },

    /**
     * A telephone number validator
     * Requires that the value matches a valid telephone number
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addTelephoneNumberValidator : function(obj, msg, erranchorelement) {
        CBSValidator.addRegexValidator(obj, msg, /^[0-9\-+. ()]+$/, erranchorelement);
    },

    /**
     * An email address validator
     * Requires that the value matches the basic syntax of a valid email address
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addEmailAddressValidator : function(obj, msg, erranchorelement) {
        CBSValidator.addRegexValidator(obj, msg, /^.+@.{2,}(\..{2,})+$/, erranchorelement);
    },

    /**
     * A custom validator.
     * A "testfunc" validation function must be passed, which accepts one
     *  parameter (the value), and returns a boolean indicating validity.
     *
     * @param string|element Element to validate
     * @param string Error message
     * @param function custom test function [must return bool and accept single string]
     * @param element|string [Optional] An element to display the error message against instead of the field itself - e.g. a label
     */
    addCustomValidator : function(obj, msg, testfunc, erranchorelement) {
        CBSValidator._addGenericValidator(obj, testfunc, msg, true, null, erranchorelement);
    },

	/**
	 * A required validator.
	 * Requires that the value exists (is >0 chars long).
	 * For a radio button, this validator requires that one button in the
	 *  group with that button's name="" is selected (but still requires
	 *  a specific element's ID or element object be passed as the first
	 *  parameter - the rest of the buttons in the group will be determined
	 *  from that element).
	 *
	 * @param string String to validate
     * @param element Element to validate (more useful for non-text fields))
     * @return boolean
	 */
	testRequired : function(value, obj) {
        if (obj) {
            value = CBS.getFieldValue(obj);
			if (obj.tagName.toLowerCase() == "select" && (obj.multiple == false || obj.multiple == undefined) && (typeof value == 'string') && value.toLowerCase() == 'null')
				return false;
			if (obj.tagName.toLowerCase() == "select" && obj.multiple && value.length == 0)
				return false;
			if (obj.tagName.toLowerCase() == 'input' && (obj.type == 'radio' || obj.type == 'checkbox')) {
				if (value !== null) return true;
				return false;
			}
            value = CBS.trimSpace(value);
			if (value.length > 0)
				return true;
			return false;
		} else {
            value = CBS.trimSpace(value);
            return (value.length > 0);
        }
	},

	/**
	 * A numeric validator.
	 * Requires that the value is a number.
	 * Uses a regular expression because isNaN ignores whitespace
	 *
	 * @param string String to validate
     * @return boolean
	 */
	testNumeric : function(value) {
	    return (/^[0-9.]*$/.test(value));
	},

	/**
	 * A range validator.
	 * Requires that the value is between "min" and "max"
	 *
	 * @param string String to validate
	 * @param float Minimum value
	 * @param float Maximum value
     * @return boolean
	 */
	testRange : function(value, min, max) {
		if (!/^[0-9.]*$/.test(value))
			return false;
		if (value > max || value < min)
			return false;
		return true;
	},

	/**
	 * A numeric integer validator.
	 * Requires that the value is a number (specifically an integer).
	 * Uses a regular expression because isNaN ignores whitespace
	 *
	 * @param string String to validate
     * @return boolean
	 */
	testNumericInteger : function(value) {
	    return (/^[0-9]*$/.test(value));
	},

	/**
	 * A range validator.
	 * Requires that the value is between "min" and "max" (and an integer)
	 *
	 * @param string String to validate
	 * @param integer Minimum value
	 * @param integer Maximum value
     * @return boolean
	 */
	testRangeInteger : function(value, min, max) {
		if (!/^[0-9]*$/.test(value))
			return false;
		if (value > max || value < min)
			return false;
		return true;
	},

	/**
	 * A length validator.
	 * Requires that the value's length is between "min" and "max"
	 *
	 * @param string String to validate
	 * @param integer Minimum length
	 * @param integer Maximum length
     * @return boolean
	 */
	testLength : function(value, min, max) {
		var vl = value.length;
		if (vl <= 0) return true;
		if (vl > max || vl < min)
			return false;
		return true;
	},

	/**
	 * A match validator.
	 * Requires that the value matches the 'refValue'
	 *
	 * @param string String to validate
	 * @param string String to match against
     * @return boolean
	 */
	testMatch : function(value, refValue) {
	    return (value == refValue);
	},

	/**
	 * A regular expression validator.
	 * Requires that the value matches the given "pattern"
	 *
	 * @param string String to validate
	 * @param regex Pattern to match against
     * @return boolean
	 */
	testRegex : function(value, pattern) {
	    return (value.length <= 0 || pattern.test(value));
	},

	/**
	 * A telephone number validator
	 * Requires that the value matches a valid telephone number
	 *
	 * @param string String to validate
     * @return boolean
	 */
	testTelephoneNumber : function(value) {
        return CBSValidator.testRegex(value, /^[0-9\-+. ()]+$/);
	},

	/**
	 * An email address validator
	 * Requires that the value matches the basic syntax of a valid email address
	 *
	 * @param string String to validate
     * @return boolean
	 */
	testEmailAddress : function(value) {
		return CBSValidator.testRegex(value, /^.+@.{2,}(\..{2,})+$/);
	},

    testDate : function(value) {
        var day=null, month=null, year=null, regex=null;
        switch (CBS_Settings.DateInputFormat) {
            case 'dd/mm/yyyy':
                if (CBSValidator.testNumericInteger(value)) {
                    //e.g. 31122009
                    regex = /^(\d{2})(\d{2})(\d{4})$/;
                } else {
                    //e.g. 31-12-2009
                    regex = /^(\d{1,2})[^\d]+(\d{1,2})[^\d]+(\d{4}|\d{2})$/;
                }
                if (regex.exec(value)) {
                    day = RegExp.$1;
                    month = RegExp.$2;
                    year = RegExp.$3;
                }
                break;
            case 'mm/dd/yyyy':
                if (CBSValidator.testNumericInteger(value)) {
                    //e.g. 12312009
                    regex = /^(\d{2})(\d{2})(\d{4})$/;
                } else {
                    //e.g. 12-31-2009
                    regex = /^(\d{1,2})[^\d]+(\d{1,2})[^\d]+(\d{4}|\d{2})$/;
                }
                if (regex.exec(value)) {
                    day = RegExp.$2;
                    month = RegExp.$1;
                    year = RegExp.$3;
                }
                break;
            case 'yyyy/mm/dd':
                if (CBSValidator.testNumericInteger(value)) {
                    //e.g. 20091231
                    regex = /^(\d{4})(\d{2})(\d{2})$/;
                } else {
                    //e.g. 2009-12-31
                    regex = /^(\d{4}|\d{2})[^\d]+(\d{1,2})[^\d]+(\d{1,2})$/;
                }
                if (regex.exec(value)) {
                    day = RegExp.$3;
                    month = RegExp.$2;
                    year = RegExp.$1;
                }
                break;
        }

        if (day && month && year) {
            //Make JS consistent with PHP, i.e. 0-69 = 2000-2069, else 1970-1999
            if (year >= 0 && year <= 69) year = 2000 + year;

            var d = new Date(year, month-1, day);
            return (d.getDate() == day && d.getMonth()+1 == month && d.getFullYear() == year);
        }
        return false;
    }
}
