/** FormCheck Class
 *  
 *  @param object config - config which generates php class FormCheck with GetJsConfig function (PEAR-JSON class should be included)
 *  @author Monin Dmitry
 *  @version 1.0.2.0
 */

(function() {
var that;

FormCheck = function(config)
{
	
	this.config = config;
	that = this;
}

FormCheck.prototype = {
	formVars : null,
	errors : {},
	AddValidation : function()
	{
		// Not yet implemeted
	},
	/** void Check(object formVars)
	 *  Checks form variables and creates array with form errors
	 *  @param object formVars - object with formvars, ie: myFormVars = {firstname: "Dmitry", lastname: "Monin"}, can be obtained with GetFormVars function
	 */
	Check : function(formVars)
	{
		this.formVars = formVars;
		this.errors = {};
		
		var fieldName = null;
		
		for(var i = 0; i < this.config.reqFields.length; i++)
		{
			fieldName = this.config.reqFields[i];
			
			if(this.config.fields.hasOwnProperty(fieldName))
			{
				// Getting validation methods
				for(var validateMethod in this.config.fields[fieldName])
				{
					if(!this.formVars.hasOwnProperty(fieldName) || !m_Validate(validateMethod, this.formVars[fieldName], this.config.fields[fieldName][validateMethod]))
					{
						this.RegisterError(fieldName, validateMethod);
					}
				}
			}
		}
	},
	/** string[] GetErrorTexts()
	 *  Returns indexed array with errors, should be used after Check() method
	 *  
	 */
	GetErrorTexts : function()
	{
		var errorTexts = [];
		for(var field in this.errors)
		{
			for(var errorType in this.errors[field])
			{
				if(!this.config.errorTexts.hasOwnProperty(field))
				{
					throw "Please specify error text for field " + field;
				}
				
				if(!this.config.errorTexts[field].hasOwnProperty(errorType))
				{
					throw "Please specify error text for field " + field + " : " + errorType;
				}
				
				errorTexts[errorTexts.length] = this.config.errorTexts[field][errorType];
				break;
			}
		}
		return errorTexts;
	},
	/** void RegisterError(string fieldName, string errorType)
	 *  Registers an error
	 *  @param string fieldName - field name
	 *  @param string errorType - error type, i.e. empty, regexp and all other custom defined errors
	 */
	RegisterError: function(fieldName, errorType)
	{
		var arr = new Array();
		if( typeof(this.errors[fieldName]) != "object" )
		{
			this.errors[fieldName] = {};
		}
		
		this.errors[fieldName][errorType] = true;
	},
	/** bool IsNotEmpty(string value)
	 *  Returns true if value is not empty
	 *  @param string value
	 *  @return bool
	 */
	IsNotEmpty : function(value)
	{
		if(typeof(value) != "string")
		{
			throw "IsNotEmpty: Value should be a string, value: " + typeof(value);
		}
		return (typeof(value) == "string" && value.length > 0);	
	},
	/** bool IsEmail(string value)
	 *  Checks value with e-mail regexp pattern
	 *  @param string value
	 *  @return bool
	 */
	IsEmail : function(value)
	{
		var pattern = "/^([A-Za-z0-9._%-]+@[A-Za-z0-9._%-]+\.[A-Za-z]{2,6})?$/i";
		return this.MatchesRegExp(pattern, value);
	},
	
	/** bool IsZipcode(string value)
	 *  Check value with zipcode pattern
	 *  @param string value
	 *  @return bool
	 */
	IsZipcode : function(value)
	{
		var ukPattern = "([a-z][a-z0-9]{1,3}\\s[0-9][a-z]{2})";
		var pattern = "/^(([0-9]{4,6})|" + ukPattern + ")?$/i";
		
		//var r = new RegExp("^(([0-9]{4,6})|" + ukPattern + ")?$", "i");

		return this.MatchesRegExp(pattern, value);
	},
	IsZipcodeDe : function(value)
	{
		var pattern = "/^([0-9]{5})?$/i";
		return this.MatchesRegExp(pattern, value);
	}
	,
	/** bool MatchesRegExp(string pattern, string value)
	 * 
	 *  @param string pattern - pattern in perl regexp format /pattern/modifiers (i.e. /[0-9]+/ig)
	 *  @param string value
	 *  @return bool
	 */
	MatchesRegExp: function(pattern, value)
	{
		var parts = pattern.split("/");
		pattern = "";
		for(var i = 1; i < parts.length-1; i++)
		{
			pattern += parts[i];
		}
		var flags = parts[parts.length - 1];
				
		var re = new RegExp(pattern, flags);
		
		return re.test(value);
	},
	/** bool IsDate(string value)
	 *  Checks if date is correct, returns true if yes
	 *  @param string value - date in format YYYY-MM-DD
	 *  @return bool
	 */
	IsDate : function(value)
	{
		if(!that.MatchesRegExp("/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/", value))
		{
			return false;
		}
		
		var dateArr = value.split("-");
		var dateObj = new Date(dateArr[0], dateArr[1] - 1, dateArr[2]);
		
		return ( (Number)(dateObj.getDate()) == (Number)(dateArr[2]) 
					&& ((Number)(dateObj.getMonth()) + 1) == (Number)(dateArr[1])  );
	},
	/** bool IsDate(string value)
	 *  Checks if date is correct, returns true if yes
	 *  @param string value - date in format YYYY-MM-DD
	 *  @return bool
	 */
	IsBirthdate : function(value)
	{
		if(!that.MatchesRegExp("/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/", value))
		{
			return false;
		}
		if(!this.IsDate(value))
		{
			return false;
		}
		var dateArr = value.split("-");
		var year = (Number)(dateArr[0]);
		var now = new Date();
		var currentYear = now.getFullYear();
		
		return (year <= currentYear && year > currentYear - 100);
	},
	/** bool HasErrors()
	 *  after checking the form returns true if form has errors and false if not.
	 *  @return bool
	 */
	HasErrors: function()
	{
		var i = 0;
		for(var err in this.errors)
		{
			i++;
		}
		return (i > 0);
	},
	/** object GetFormVars(string/object formId)
	 *  Returns an object with form variables. 
	 *  @param string/object formId - form id or form object
	 */
	GetFormVars : function (formId)
	{
		var oForm;
		if(typeof formId == 'string'){
			// Determine if the argument is a form id or a form name.
			// Note form name usage is deprecated by supported
			// here for legacy reasons.
			oForm = (document.getElementById(formId) || document.forms[formId]);
		}
		else if(typeof formId == 'object'){
			// Treat argument as an HTML form object.
			oForm = formId;
		}
		else {
			throw "formId should be a form object or form id";
		}
			
		var oElement, oName, oValue, oDisabled, formVars = {};
			
		// Iterate over the form elements collection to construct the
		// label-value pairs.
		for (var i=0; i<oForm.elements.length; i++){
			oElement = oForm.elements[i];
			oDisabled = oForm.elements[i].disabled;
			oName = oForm.elements[i].name;
			oValue = oForm.elements[i].value;
	
			// Do not submit fields that are disabled or
			// do not have a name attribute value.
			if(!oDisabled && oName)
			{
				switch (oElement.type)
				{
					case 'select-one':
					case 'select-multiple':
						formVars[oName] = oValue;
						break;
					case 'radio':
					case 'checkbox':
						if(oElement.checked)
						{
							formVars[oName] = oValue;
						}
						break;
					case 'file':
						// stub case as XMLHttpRequest will only send the file path as a string.
					case undefined:
						// stub case for fieldset element which returns undefined.
					case 'reset':
						// stub case for input type reset button.
					case 'button':
						// stub case for input type button elements.
						break;
					case 'submit':
						// stub case for input type submit elements.
						break;
					default:
						formVars[oName] = oValue;
						break;
				}
			}
		}
		
		return formVars;
	}
}

// Private members

function m_Validate(validateMethod, value, parameter)
{
	// Value must be a string
	if(typeof(value) != "string")
	{
		throw "m_validate: Value should be as tring, typeof: " + typeof(value);
	}
	
	switch(validateMethod)
	{
		case "empty":
			return that.IsNotEmpty(value);
		case "email":
			return that.IsEmail(value);
		case "zipcode":
			return that.IsZipcode(value);
		case "zipcode_de":
			return that.IsZipcodeDe(value);
			break;
		case "regexp":
			return that.MatchesRegExp(parameter, value);
		case "date":
			return that.IsDate(value);
		case "birthdate":
			return that.IsBirthdate(value);
		default:
			return true;
	}
}

})();
