/* JQUERY JAVASCRIPT VALIDATION LIBRARY
*
*  Validation object provides validation for the following:
*    - Alphabetic
*    - Alphanumeric
*    - Numeric
*    - Password (alphanumeric + special)
*    - Email address
*    - Phone numbers
*    - Postcodes (UK)
*    - Date
*    - Time
*
*  Format for validation object:
*    jQuery.validation.validate (field ID, type, error message [, required, length, depends {field:field ID, value:value}, error function])
*    jQuery.validation.compare  (field ID, field ID, comparison:<=>, error message [, error function])
*    jQuery.validation.mexclude ({field IDs}, error message [, required, error function])
*
*    jQuery.validation.validate:
*      - id:             DOM Id of an input node(s). If an array of nodes is given, the values of the inputs will be concatenated.
*      - type:           Format of input to validate against. This can be one of the predefined functions contained in this object, or a custom
*                          error function passed to the object as a function
*      - error_message:  Error message to be displayed should the validation fail
*      - required:       The input is required. Note that for dropdowns (select) the value for an invalid selection should be empty
*      - length:         The length in characters of the data. If only a number is supplied, this will validate the minimum length of the value
*                          of the field value. If an array of two numbers is passed, these will be the minumum and maximum values (inclusive)
*                          respectively e.g. [3,8]
*      - depends:	     An array containing the DOM Id of the required field and its value necessary for this field to validate
*      - error_function: A custom error function which will execute if validation fails. A global function can be set using the external function:
*                          ES_VAL.setErrFn ( error function )
*
*    jQuery.validation.compare:
*      - id:             DOM Id of an input node to be compared to. Note that the fields must be of equal value type (text, boolean)
*      - compare:        DOM Id of an input node to be compared
*      - comparison:     A comparator saying whether the value of the first field should be greater, less than or equal than that of the second field
*      - error_message:  Error message to be displayed should the comparison fail
*      - error_function: A custom error function which will execute if validation fails. A global function can be set using the external function:
*                          ES_VAL.setErrFn ( error function )
*
*    jQuery.validation.mexclude:
*      - ids:            Array of DOM Ids of input nodes that should be mutually exclusive i.e. only one can be filled out
*      - error_message:  Error message to be displayed should the comparison fail
*      - required:       One of the mutually exlusive input nodes is required
*      - depends:	     An array containing the DOM Id of the required field and its value necessary for this field to validate
*      - error_function: A custom error function which will execute if validation fails. A global function can be set using the external function:
*                          ES_VAL.setErrFn ( error function )
*
*  Custom validation functions:
*    For these to work, they need to return either TRUE or FALSE depending on whether validation passes or fails. An external validation function is
*      passed to the object and then custom validation types can be used. Note that builtin validation types are prefixed with "is_". The external
*      validation function is passed to the object as:
*        object.setValidationFn ( validation function )
*
*  Error functions:
*    If neither this error function nor the global error function is not specified, the object will fall back to an alert
*    containing the error message defined. As custom error message can be set with:
*      object.setProcessFn ( error function )
*
*  Field specific custom error functions:
*    These can do anything and will be a self-contained function. They will accept a DOM reference(s) to the fields which do not validate
*    Note that they will override the global user defined error function if one has been passed to the validation object
*
*  Prepopulated fields:
*
*
*
*********************************************/

jQuery.validation = function( data )
{
	var _object;

	var _validations = [];
	var _comparisons = [];
	var _mexclusions = [];

	var _form = data.form;
	var _processFn = ( data.error && typeof data.error == 'function' ) ? data.error : function ( valid, dom_field, message) { if (!valid) alert(message) };
	var _validationFn = null;
	var _auto = data.auto || false;

	var _events = { submit:'submit', change:'change' };
	var _event = _events[ data.event ] || 'submit';

	this.validate			= _validate;
	this.compare			= _compare;
	this.mexclude			= _mexclude;
	this.validateForm		= _validateForm;
	this.setProcessFn		= _setProcessFn;
	this.setValidationFn	= _setValidationFn;


	// Default validation type functions
	function _is_string		( value ) { return (!value.toString().match(/^[\s]+$/))? true : false; }
	function _is_alpha		( value ) { return (value.toString().match(/^[a-zA-Z]*$/))? true : false; }
	function _is_alphanum	( value ) { return (value.toString().match(/^[a-zA-Z0-9\s]*$/))? true : false; }
	function _is_number		( value ) { return (value.toString().match(/^[0-9]*$/))? true : false; }
	function _is_name		( value ) { return (value.toString().match(/^[a-zA-Z\s\-\.\']*$/)&&!value.toString().match(/^[\s]+$/))? true : false ; }
	function _is_password	( value ) { return (value.toString().match(/^(?:\d+[a-z]|[a-z]+\d)[a-z\d]*$/i))? true : false; }
	function _is_email		( value ) { return (value.toString().match(/(^[a-zA-Z0-9\.\-_]+)@([a-zA-Z0-9\.\-_])+\.([a-zA-Z]+)$/))? true : false; }
	function _is_phone		( value ) { return (value.toString().match(/(^[0-9\+\(\)\s]+)$/))? true : false; }
	function _is_date		( value ) { value=value.toString().replace(/[\/\.-]/g, '');var t=new Date();t.setFullYear(parseInt(value.substr(4,4),10),(parseInt(value.substr(2,2),10)-1),parseInt(value.substr(0,2),10));return ( value.match(/^[0-9]{8}$/) && t.getMonth()==(parseInt(value.substr(2,2),10)-1) && t.getFullYear()==parseInt(value.substr(4,4),10) )? true : false; }
	function _is_past_date	( value ) { var t=new Date();return ( _is_date && value <= t.getFullYear() ) ? true : false }
	function _is_time		( value ) { value=value.toString().replace(/[:\.]/g, '');if(value.length<3)return false;while(value.length<4)value='0'+value;if(value.match(/^[0-9]{4}$/)){var h=parseInt(value.substr(0,2));var m=parseInt(value.substr(2,2));return h<24&&h>=0&&m<60&&m>=0}else return false }
	function _is_true		( value ) { return (value)? true : false; }
	function _is_yob		( value ) { var year = new Date(); return (_is_number(value) && value <= year.getFullYear()); }
    function _is_quantity	( value ) { return (value >0) && (value.toString().match(/^[0-9]*$/))? true : false; }
	function _is_validFile	( value ) {
	
		if(value!=null&&value.toString()!=''){	
			var f_name=value.toString();
			var ext=f_name.substr(f_name.length-3);
			if(ext.toLowerCase()=='pdf'){
				return true;
			}
			return false;
		}
		return false;
	}

	/* INITIALISATION */

	// add onload event to the form for validation on submit
	if ( _auto ) jQuery( document ).ready( _setupForm );

	// save this object reference
	_object = this;



	/* METHODS */

	// Set up auto form for validation on submit
	function _setupForm()
	{
		jQuery( _form ).bind( 'submit', _validateForm );
	}

	// Main Validation function
	function _validate ( parameters ) // { id, type, required, length, depends, error_message, error_function }
	{
		// push the validation onto the array
		var index = _validations.length;
		_validations[ index ] = parameters;

		if ( _event == 'change' ) jQuery( document ).ready( function()
		{
			var event = ( jQuery.browser.msie && jQuery( '#'+parameters.id ).is( ':radio,:checkbox' ) ) ? 'click' : _event;
			jQuery( '#'+parameters.id ).bind( event, function(e) { _validateItem( index, e ) } )
		} );
	}

	function _compare ( parameters ) // { id, compare, comparator, error_message, error_function }
	{
		// push the validation onto the array
		var index = _comparisons.length;
		_comparisons[ index ] = parameters;

		if ( _event == 'change' ) jQuery( document ).ready( function()
		{
			var event = ( jQuery.browser.msie && jQuery( '#'+parameters.id ).is( ':radio,:checkbox' ) ) ? 'click' : _event;
			jQuery( '#'+parameters.id ).bind( event, function(e) { _compareItem( index, e ) } )
		} );
	}

	function _mexclude ( parameters ) // { ids, required, depends, error_message, error_function }
	{
		// push the validation onto the array
		var index = _mexclusions.length;
		_mexclusions[ index ] = parameters;

		if ( _event == 'change' ) jQuery( document ).ready( function()
		{
			var event = ( jQuery.browser.msie && jQuery( '#'+parameters.id ).is( ':radio,:checkbox' ) ) ? 'click' : _event;
			jQuery( '#'+parameters.id ).bind( event, function(e) { _mexcludeItem( index, e ) } )
		} );
	}


	function _validateItem(index,e)
	{
		var validation = _validations[index];

		// get validation data
		var dom_id     = validation.id;
		var v_type     = validation.type;
		var e_message  = validation.error_message;
		var required   = ( validation.required )? validation.required : false;
		var length     = ( validation.length )? validation.length : null;
		var depends    = ( validation.depends )? validation.depends : null;
		var trim       = ( validation.trim )? true : false;
		var e_function = ( validation.error_function )? validation.error_function : null;

		// get field data (if there are more than one field, concatenate them)
		if (typeof dom_id == 'string')
		{
			var input = document.getElementById(dom_id);
			var value = ( input )? _getValue(input) : false;
			if ( trim ) value = value.replace( /^\s+|\s+$/g, '' );
		}
		else if (typeof dom_id == 'object' && dom_id.constructor == Array)
		{
			var input = document.getElementById(dom_id[0]);
			var value = '';

			// check if inputs exist
			for (var z = 0; z < dom_id.length; z++)
				if ( !document.getElementById(dom_id[z]) ) input = false;

			// concatenate input values (note, you cannot concatenate boolean values!)
			if ( input )
				for (var z = 0; z < dom_id.length; z++)
					value += _getValue(document.getElementById(dom_id[z]));
		}

		// if the input exists, then validate it
		if ( input )
		{
			// set up validation variables
			var is_valid = false;
			var has_value = false;
			var depended = false;

			// check if it is required (empty or not)
			has_value = _hasValue(input);

			// check if this field is dependent on another(s)
			var required_dependency = _isDependent(depends);

			// check if this field is the required length
			var correct_length = _isLength(length, value);

			// check if field validates
			if ( has_value && required_dependency && correct_length )
			{
				// check if the type is an inbuilt one ( "is_" prefix )
				if ( v_type.substr(0, 3) == 'is_' )
				{
					is_valid = _isValid(v_type, value);
				}
				// or try an external validation function if it exists
				else
				{
					//if ( _validationFn ) is_valid = _validationFn(v_type, value);
					if ( eval( v_type ) ) is_valid = eval( v_type + '(value)' );
					else is_valid = false;
				}
			}
			else
			{
				if ( has_value && required_dependency && !correct_length )
					is_valid = false;
				else
				{
					if ( !has_value && required && required_dependency ) is_valid = false;
					else if ( (!has_value && !required) || !required_dependency ) is_valid = true;
				}
			}

			// if valid and trim set, trim
			if ( is_valid && trim ) input.value = input.value.replace( /^\s+|\s+$/g, '' );

			// process the validation for display on page
			if (!e_function) _processFn ( is_valid, input, e_message );
			else e_function ( is_valid, input, e_message );

			return is_valid;
		}

		return false;
	}


	function _compareItem(index,e)
	{
		var comparison = _comparisons[index];

		// get comparison data
		var dom_id1    = comparison.id;
		var dom_id2    = comparison.compare;
		var comparator = comparison.comparator;
		var depends    = ( comparison.depends )? comparison.depends : null;
		var trim       = ( comparison.trim )? true : false;
		var e_message  = comparison.error_message;
		var e_function = ( comparison.error_function )? comparison.error_function : null;

		// set up validation variables
		var is_valid = false;

		// set input variables
		var input1 = document.getElementById(dom_id1);
		var input2 = document.getElementById(dom_id2);

		// if the inputs exist, then compare them
		if ( input1 && input2 )
		{
			// get the values of the fields
			var value1 = _getValue(input1);
			var value2 = _getValue(input2);
			if ( trim ) value1 = value1.replace( /^\s+|\s+$/g, '' );
			if ( trim ) value2 = value2.replace( /^\s+|\s+$/g, '' );

			// check if this field is dependent on another(s)
			var required_dependency = _isDependent(depends);

			if ( required_dependency )
			{
				// if the comparator is valid, then compare
				if (comparator == '=' || comparator == '<' || comparator == '>')
				{
					if (comparator == '=') comparator = '==';
					is_valid = eval ('value1' + comparator + 'value2');
				}
			}
			else is_valid = true;

			// if valid and trim set, trim
			if ( is_valid && trim ) input.value = input.value.replace( /^\s+|\s+$/g, '' );

			// process the validation for display on page for each mutually exclusive field
			if (!e_function) _processFn ( is_valid, [input1, input2], e_message );
			else e_function ( is_valid, input1, e_message );

			// cancel form submit if invalid
			return is_valid;
		}

		return false;
	}


	function _mexcludeItem(index,e)
	{
		var mexclusion = _mexclusions[index];

		// get mutually exclusive data
		var dom_ids    = mexclusion.ids;
		var e_message  = mexclusion.error_message;
		var required   = ( mexclusion.required )? mexclusion.required : false;
		var depends    = ( mexclusion.depends )? mexclusion.depends : null;
		var trim       = ( mexclusion.trim )? true : false;
		var e_function = ( mexclusion.error_function )? mexclusion.error_function : null;

		// set up validation variables
		var is_valid = false;

		// check if inputs exist
		var inputs_exist = true;
		for (z = 0; z < dom_ids.length; z++) if ( !document.getElementById(dom_ids[z]) ) inputs_exist = false;

		// check if this field is dependent on another(s)
		var required_dependency = _isDependent(depends);

		// if the inputs exist, then check them
		if ( inputs_exist )
		{
			// check the values of the fields, to see if more than one is set
			var values = 0;
			for (var y = 0; y < dom_ids.length; y++) if ( _hasValue ( document.getElementById(dom_ids[y]) ) ) values++;

			if (values < 2) is_valid = true;
			else if (values == 0 && !required_dependency) is_valid = true;
			if (values == 0 && required && required_dependency) is_valid = false;

			// if valid and trim set, trim
			if ( is_valid && trim ) input.value = input.value.replace( /^\s+|\s+$/g, '' );

			// process the validation for display on page for each mutually exclusive field
			if (!e_function) _processFn ( is_valid, _idToDOMObject(dom_ids), e_message );
			else e_function ( is_valid, _idToDOMObject(dom_ids[0]), e_message );

			// cancel form submit if invalid
			return is_valid;
		}

		return false;
	}


	function _validateForm ( e )
	{
		var valid = true;
		var focus_field = false;

		// FIRST PERFORM FIELD TYPE VALIDATION
		for (var x = 0; x < _validations.length; x++)
		{
			// get validation data
			var dom_id     = _validations[x].id;
			var v_type     = _validations[x].type;
			var e_message  = _validations[x].error_message;
			var required   = ( _validations[x].required )? _validations[x].required : false;
			var length     = ( _validations[x].length )? _validations[x].length : null;
			var depends    = ( _validations[x].depends )? _validations[x].depends : null;
			var trim       = ( _validations[x].trim )? true : false;
			var e_function = ( _validations[x].error_function )? _validations[x].error_function : null;

			// get field data (if there are more than one field, concatenate them)
			if (typeof dom_id == 'string')
			{
				var input = document.getElementById(dom_id);
				var value = ( input )? _getValue(input) : false;
				if ( trim ) value = value.replace( /^\s+|\s+$/g, '' );
			}
			else if (typeof dom_id == 'object' && dom_id.constructor == Array)
			{
				var input = document.getElementById(dom_id[0]);
				var value = '';

				// check if inputs exist
				for (var z = 0; z < dom_id.length; z++)
					if ( !document.getElementById(dom_id[z]) ) input = false;

				// concatenate input values (note, you cannot concatenate boolean values!)
				if ( input )
					for (var z = 0; z < dom_id.length; z++)
						value += _getValue(document.getElementById(dom_id[z]));
			}

			// if the input exists, then validate it
			if ( input )
			{
				// set up validation variables
				var is_valid = false;
				var has_value = false;
				var depended = false;

				// check if it is required (empty or not)
				has_value = _hasValue(input);

				// check if this field is dependent on another(s)
				var required_dependency = _isDependent(depends);

				// check if this field is the required length
				var correct_length = _isLength(length, value);

				// check if field validates
				if ( has_value && required_dependency && correct_length )
				{
					// check if the type is an inbuilt one ( "is_" prefix )
					if ( v_type.substr(0, 3) == 'is_' )
					{
						is_valid = _isValid(v_type, value);
					}
					// or try an external validation function if it exists
					else
					{
					 	//if ( _validationFn ) is_valid = _validationFn(v_type, value);
 						if ( eval( v_type ) ) is_valid = eval( v_type + '(value)' );
						else is_valid = false;
					}
				}
				else
				{
					if ( has_value && required_dependency && !correct_length )
						is_valid = false;
					else
					{
						if ( !has_value && required && required_dependency ) is_valid = false;
						else if ( (!has_value && !required) || !required_dependency ) is_valid = true;
					}
				}

				// process the validation for display on page
				if (!e_function) _processFn ( is_valid, input, e_message );
				else e_function ( is_valid, input, e_message );

				// if valid and trim set, trim
				if ( is_valid && trim ) input.value = input.value.replace( /^\s+|\s+$/g, '' );

				// if not valid, then focus the first field
				if ( !focus_field && !is_valid )
				{
					if(input.visibility == 'visible' || input.visibility == 'show') input.focus();
					focus_field = true;
				}

				// cancel form submit if invalid
				var valid = (valid)? is_valid : false;
			}
		}

		// THEN PERFORM FIELD COMPARISONS
		for (x = 0; x < _comparisons.length; x++)
		{
			// get comparison data
			var dom_id1    = _comparisons[x].id;
			var dom_id2    = _comparisons[x].compare;
			var comparator = _comparisons[x].comparator;
			var depends    = ( _comparisons[x].depends )? _comparisons[x].depends : null;
			var trim       = ( _comparisons[x].trim )? true : false;
			var e_message  = _comparisons[x].error_message;
			var e_function = ( _comparisons[x].error_function )? _comparisons[x].error_function : null;

			// set up validation variables
			var is_valid = false;

			// set input variables
			var input1 = document.getElementById(dom_id1);
			var input2 = document.getElementById(dom_id2);

			// if the inputs exist, then compare them
			if ( input1 && input2 )
			{
				// get the values of the fields
				var value1 = _getValue(input1);
				var value2 = _getValue(input2);
				if ( trim ) value1 = value1.replace( /^\s+|\s+$/g, '' );
				if ( trim ) value2 = value2.replace( /^\s+|\s+$/g, '' );

				// check if this field is dependent on another(s)
				var required_dependency = _isDependent(depends);

				if ( required_dependency )
				{
					// if the comparator is valid, then compare
					if (comparator == '=' || comparator == '<' || comparator == '>')
					{
						if (comparator == '=') comparator = '==';
						is_valid = eval ('value1' + comparator + 'value2');
					}
				}
				else is_valid = true;

				// if valid and trim set, trim
				if ( is_valid && trim ) input.value = input.value.replace( /^\s+|\s+$/g, '' );

				// process the validation for display on page for each mutually exclusive field
				if (!e_function) _processFn ( is_valid, [input1, input2], e_message );
				else e_function ( is_valid, input1, e_message );

				// cancel form submit if invalid
				var valid = (valid)? is_valid : false;
			}
		}

		// FINALLY PERFORM MUTUALLY EXCLUSIVE VALIDATIONS
		for (x = 0; x < _mexclusions.length; x++)
		{
			// get mutually exclusive data
			var dom_ids    = _mexclusions[x].ids;
			var e_message  = _mexclusions[x].error_message;
			var required   = ( _mexclusions[x].required )? _mexclusions[x].required : false;
			var depends    = ( _mexclusions[x].depends )? _mexclusions[x].depends : null;
			var trim       = ( _mexclusions[x].trim )? true : false;
			var e_function = ( _mexclusions[x].error_function )? _mexclusions[x].error_function : null;

			// set up validation variables
			var is_valid = false;

			// check if inputs exist
			var inputs_exist = true;
			for (z = 0; z < dom_ids.length; z++) if ( !document.getElementById(dom_ids[z]) ) inputs_exist = false;

			// check if this field is dependent on another(s)
			var required_dependency = _isDependent(depends);

			// if the inputs exist, then check them
			if ( inputs_exist )
			{
				// check the values of the fields, to see if more than one is set
				var values = 0;
				for (var y = 0; y < dom_ids.length; y++) if ( _hasValue ( document.getElementById(dom_ids[y]) ) ) values++;

				if (values < 2) is_valid = true;
				else if (values == 0 && !required_dependency) is_valid = true;
				if (values == 0 && required && required_dependency) is_valid = false;

				// if valid and trim set, trim
				if ( is_valid && trim ) input.value = input.value.replace( /^\s+|\s+$/g, '' );

				// process the validation for display on page for each mutually exclusive field
				if (!e_function) _processFn ( is_valid, _idToDOMObject(dom_ids), e_message );
				else e_function ( is_valid, _idToDOMObject(dom_ids[0]), e_message );

				// cancel form submit if invalid
				var valid = (valid)? is_valid : false;
			}
		}

		// submit only if validation is good
		if ( !valid )
		{
			if ( arguments.length ) e.preventDefault();
			return false;
		}
		else return true;
	}


	function _isValid ( type, value )
	{
		var fn = eval( '_' + type );
		return fn( value );
	}

	function _hasValue ( dom_input )
	{
		var value;

		if ((dom_input.nodeName == 'INPUT' && (dom_input.type == 'text' || dom_input.type == 'password' || dom_input.type == 'hidden')) || dom_input.nodeName == 'SELECT' || dom_input.nodeName == 'TEXTAREA')
			value = !jQuery( dom_input ).data( 'default' ) ? dom_input.value : ( dom_input.value == jQuery( dom_input ).data( 'default' ) ? '' : dom_input.value );
		else if (dom_input.nodeName == 'INPUT' && (dom_input.type == 'radio' || dom_input.type == 'checkbox'))
			value = dom_input.checked;
		else value = jQuery( dom_input ).val();

		return ( value )? true : false;
	}

	function _isDependent ( dependencies )
	{
		if ( dependencies )
		{
			var satisfies_dependency = true;

			// check if one dependency has been passed, and if its only one, put it in an array
			if ( dependencies.constructor != Array )
				dependencies = new Array ( dependencies );

			// check each dependor field value matches its condition value
			for (var z = 0; z < dependencies.length; z++)
			{
				if ( document.getElementById(dependencies[z].id) )
				{
					var dom_depend = document.getElementById(dependencies[z].id);
					var depend_value = _getValue(dom_depend);

					if ( typeof dependencies[z].value == 'string' && depend_value != dependencies[z].value ) satisfies_dependency = false;
					else if ( typeof dependencies[z].value == 'boolean' && dependencies[z].value != _hasValue(dom_depend) ) satisfies_dependency = false;
				}
			}

			return satisfies_dependency;
		}
		else return true;
	}

	function _isLength ( length, value )
	{
		if ( length )
		{
			if ( typeof length == 'object' && length.constructor == Array )
			{
				var min = length[0];
				var max = length[1];

				if ( value.toString().length >= min && value.toString().length <= max )
					return true;
				else
					return false;
			}
			else if ( typeof length == 'number' )
			{
				var min = length;

				if ( value.toString().length >= min )
					return true;
				else
					return false;
			}
			else return false;
		}
		else return true;
	}

	function _getValue ( dom_input )
	{
		var value;

		if ((dom_input.nodeName == 'INPUT' && (dom_input.type == 'text' || dom_input.type == 'password' || dom_input.type == 'hidden')) || dom_input.nodeName == 'SELECT' || dom_input.nodeName == 'TEXTAREA')
			value = dom_input.value;
		else if (dom_input.nodeName == 'INPUT' && (dom_input.type == 'radio' || dom_input.type == 'checkbox'))
			value = dom_input.checked;
		else value = jQuery( dom_input ).val();

		return value;
	}

	function _setProcessFn ( fn )
	{
		// set a custom error function
		_processFn = fn;
	}

	function _setValidationFn ( fn )
	{
		// set a custom validation function
		_validationFn = fn;
	}


	// GENERIC FUNCTIONS

	// Return a DOM object or an array of DOM objects for DOM ID(s)
	function _idToDOMObject ( dom_id )
	{
		if (typeof dom_id == 'string')
			return document.getElementById(dom_id);
		else
		{
			var dom_ids = new Array();
			for (var z = 0; z < dom_id.length; z++)
				dom_ids[dom_ids.length] = document.getElementById(dom_id[z]);
			return dom_ids;
		}
	}
}
