/* USAGE
example class name: jj_y_n_0_3m45  (Initials_Required_Type_Format[_Length]) Length is optional

Initials - needed to determine if the class name is used for validation
	can be any string of alpha-numeric characters as defined by the variable "initials"
	
Required - needed to determine if the value is required for submission
	y = required
	n = not required, but if there is data in the field then validate it
	
Type - used to determine the validation type to be used on the elements data
	n = number
	s = string
	p = phone/fax number
	d = date
	e = email address
	z = zip code
	f = file
	
Format - needed to determine how the elements data should be formatted
	SEE "FORMATS" section
	
Length (optional) - used to validate the length of the elements data
	first number is the minumum value; second number is the maximum value
	the "m" character is needed separate the minimum and maximum values
	example: 3m45  (min val is 3, max val is 45)
*/



function FormValidator(form_id,v_type) {
	this.form_obj = document.getElementById(form_id);
	this.v_type = v_type; // 'form' or a passed field name
	this.err_type = 'alert'; // 'alert' or 'dom'
	
	this.initials = 'jj_';
	
	this.checked_array = new Array();
	this.params = new Array();
	this.el_type='';
	this.el_name='';
	this.el_params='';
	this.el_value='';
	
	// ############### error display variables ###############
	this.default_color = '#f1f293';
	this.error_color = '#ff0000';
	this.has_errors = false;
	this.error_string = '';
	this.error_block = '';
	this.errors = new Array();
	this.error_ids = new Array();
	this.pre_error_string = 'The following errors have been detected:' + "\n\n";
	this.post_error_string = "\n\n" +'Please fix all errors and re-submit.';
	this.errors_list = new Array();
	this.errors_list[0] = ' was left empty.';
	this.errors_list[1] = ' is formatted wrong.';
	this.errors_list[2] = ' must be a whole number';
	this.errors_list[3] = ' is less then the minimum value allowed.';
	this.errors_list[4] = ' is less then the minimum characters required.';
	this.errors_list[5] = ' is not a valid date';
	this.errors_list[6] = ' was not checked';
	this.errors_list[7] = ' was not selected';
	this.errors_list[8] = ' is greater then the maximum value allowed.';
	this.errors_list[9] = ' is greater then the maximum characters required.';
	this.errors_list[10] = ' is not an accepted file format.';
	// ##############################################################
	
	// ############### FORMATS - validation regular expressions ###############
	// (z) ZIP CODES
	this.zip_formats=new Array();
	this.zip_formats[0] = /^\d{5}(\-\d{4})?$/; // xxxxx or xxxxx-xxxx
	this.zip_formats[1] = /^(\d{5}|\d{6})(\-\d{4})?$/; // xxxxx or xxxxx-xxxx or xxxxxx
	// (d) DATES 
	this.date_formats=new Array();
	this.date_formats[0] = /^\d{2}(\-|\/|\.)\d{2}\1\d{2}$/; // mm-dd-yy or mm/dd/yy or mm.dd.yy
	this.date_formats[1] = /^\d{2}(\-|\/|\.)\d{2}\1\d{4}$/; // mm-dd-yyyy or mm/dd/yyyy or mm.dd.yyyy
	this.date_formats[2] = /^\d{4}(\-|\/|\.)\d{2}\1\d{2}$/; // yyyy-mm-dd or yyyy/mm/dd or yyyy.mm.dd
	// (s) STRINGS
	this.string_formats=new Array();
	this.string_formats[0] = /[*]/; // any alphanumeric character and the underscore
	this.string_formats[1] = /[^a-zA-Z\s]/; // any alphabetic character
	this.string_formats[2] = /[*]/; // any alphanumaeric character  (generally for passwords)
	// (e) EMAILS
	this.email_formats=new Array();
	this.email_formats[0] = /^(\w+(?:\.\w+)*)@((?:\w+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
	// (n) NUMBERS
	this.number_formats=new Array();
	this.number_formats[0] = /^\d+$/; // any whole number
	// (p) PHONE / FAX
	this.phone_formats=new Array();
	this.phone_formats[0] = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,3})|(\(?\d{2,3}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/; //accepts phone number in both local format (eg. 02 1234 5678 or 123 123 4567) or international format (eg. +61 (0) 2 1234 5678 or +1 123 123 4567). It also accepts an optional extention of up to five digits prefixed by x or ext (eg. 123 123 4567 x89).
	this.phone_formats[1] = this.phone_formats[0];
	// (f) FILENAME
	this.file_formats=new Array();
	this.file_formats[0] = /\.(jpg)|(jpeg)$/i;// jpg or jpeg
	this.file_formats[1] = /\.(png)$/i;// png
	this.file_formats[2] = /\.(gif)$/i;// gif
	// ##############################################################
		
	this.validate = function () {
		var i,j,k,checked;
		
		//close dom alert window if open
		if (document.getElementById('alert_div')) {
			var alert_div = document.getElementById('alert_div');
			document.body.removeChild(alert_div);
		}
		// revert field titles back to default color
		this.revert_fields();
		
		if (this.v_type == 'form') { // validate entire form
			for (i=0;i<this.form_obj.length;i++) {
				this.el_type = this.form_obj.elements[i].type;
				this.el_name = this.form_obj.elements[i].name;
				this.el_value = this.form_obj.elements[i].value;
				//get the params - if any
				if (this.form_obj.elements[i].className) {
					if (this.verify_params(this.form_obj.elements[i].className)) {
						this.get_params(this.el_params);
						// make sure it has not been checked yet (for radio groups)
						for (j=0;j<this.checked_array.length;j++) {
							checked = (this.checked_array[j] == this.el_name) ? true : false;
						}
						if (!checked) { this.validate_value(); }
						this.checked_array[this.checked_array.length] = this.el_name;
					}
				}
			}
		} else { // validate single value
			this.el_type = this.form_obj[this.v_type].type;
			this.el_name = this.form_obj[this.v_type].name;
			this.el_value = this.form_obj[this.v_type].value;
			//get the params - if any
			if (this.form_obj[this.v_type].className) {
				if (this.verify_params(this.form_obj[this.v_type].className)) {
					this.get_params(this.el_params);
					this.validate_value();
				}
			}
		}
		return (this.has_errors) ? false : true;
	}
	
	this.validate_value = function() {
		switch (this.el_type) {
			case 'text' :
				this.check_text();
				break;
			case 'select-one' :
				this.check_select();
				break;
			case 'select-multiple' :
				this.check_select();
				break;
			case 'checkbox' :
				this.check_checkbox();
				break;
			case 'radio' :
				this.check_radio();
				break;
			case 'textarea' :
				this.check_text();
				break;
			case 'password' :
				this.check_text();
				break;
			case 'file' :
				this.check_text();
				break;
			default:
				
				break;
			}
	}

	this.report_errors = function() {
		var i;
		switch (this.err_type) {
		case 'alert' :
			this.error_string = this.pre_error_string;
			for (i=0;i<this.errors.length;i++) {
			this.error_string += (i+1) + '. ' + this.errors[i] + "\n";
			}
			this.error_string += this.post_error_string;
			window.alert(this.error_string);
			break;
		case 'dom' :
			var alert_div = document.createElement('div');
			alert_div.setAttribute('id','alert_div');
			
			var pre_warn = document.createElement('h2');
			var insert_pre_warn = alert_div.appendChild(pre_warn);
			var pre_warn_text = document.createTextNode(this.pre_error_string);
			var insert_pre_warn_text = insert_pre_warn.appendChild(pre_warn_text);
			
			var err_list = document.createElement('ol');
			var insert_err_list = alert_div.appendChild(err_list);
			
			for (i=0;i<this.errors.length;i++) {
				var err_list_item = document.createElement('li');
				var err_text = document.createTextNode(this.errors[i]);
				var insert_list_item = insert_err_list.appendChild(err_list_item);
				var insert_list_item_text = insert_list_item.appendChild(err_text);
			}
			
			var post_warn = document.createElement('h3');
			var insert_post_warn = alert_div.appendChild(post_warn);
			var post_warn_text = document.createTextNode(this.post_error_string);
			var insert_post_warn_text = insert_post_warn.appendChild(post_warn_text);			
			
			var link = document.createElement("a");
			link.setAttribute("href","javascript:close_alert();");
			var insert_link = alert_div.appendChild(link);
			var link_text = document.createTextNode('close');
			var insert_link_text = insert_link.appendChild(link_text);
			
			document.body.appendChild(alert_div);
			
			var top_offset = (window.pageYOffset) ? window.pageYOffset + 'px' : document.body.scrollTop + 'px';
			document.getElementById('alert_div').style.marginTop = top_offset;
		
			break;
		}
		// change color of field names that have errors
		for (k=0;k<this.error_ids.length;k++) {
			var label_id = this.error_ids[k] + '_label';
			if (document.getElementById(label_id)) {
				document.getElementById(label_id).style.borderLeftWidth='4px';
				document.getElementById(label_id).style.paddingLeft='4px';
				document.getElementById(label_id).style.borderLeftStyle='solid';
				document.getElementById(label_id).style.borderLeftColor=this.error_color;
			}
		}
	}

	this.revert_fields = function() {
		var i;
		for (i=0;i<this.form_obj.length;i++) {
			if (document.getElementById(this.form_obj[i].name+'_label')) {
				document.getElementById(this.form_obj[i].name+'_label').style.borderLeftColor=this.default_color;
			}
		}
	}

	this.add_error = function(error_num) {
		this.has_errors = true;
		this.errors[this.errors.length] = this.get_display_name(this.el_name) + this.errors_list[error_num];
		this.error_ids[this.error_ids.length] = this.el_name;
	}
	
	this.verify_params = function(class_name) {
		this.el_params = '';
		if (class_name != '') {
			if (class_name.indexOf(' ') != -1) {
				var class_name_array = class_name.split(' ');
				var class_len = class_name_array.length;
				for (k=0;k<class_len;k++) {
					if (class_name_array[k].indexOf(this.initials) != -1) {
						this.el_params = class_name_array[k].substr(3);
					}
				}
			} else {
				if (class_name.indexOf(this.initials) != -1) {
					this.el_params = class_name.substr(3);
				}
			}
		}
		return (this.el_params != '') ? true : false;
	}
	
	this.get_params = function(params) { //ex. - y_n_0_3m45
		var params_array,len_array,min_max_vals;
		this.params = new Array();//reset parameters array
		params_array = params.split('_');
		this.params['required'] = params_array[0]; //ex. - y=required
		this.params['type'] = params_array[1]; //ex. - n=number
		this.params['format'] = params_array[2]; //ex. - 0
		if (params_array.length > 3) {
			min_max_vals = params_array[3];
			if (min_max_vals.indexOf('m') != -1) { //ex. - 3m45  (min val is 3, max val is 45)
				len_array = min_max_vals.split('m');
				this.params['min_length'] = len_array[0] - 0; //ex. -  3 or -3
				this.params['max_length'] = len_array[1] - 0;
			} else {
				this.params['min_length'] = min_max_vals - 0;
			}
		}
		return true;
	}

	this.get_display_name = function(name) {
		var i,display_name='',tmp_array,spacer = ' ';
		tmp_array = name.split('_');
		for (i=0;i<tmp_array.length;i++) {
			display_name += spacer + tmp_array[i];
		}
		return display_name.substr(1,display_name.length-1);
	}

	this.check_text = function() {
		if (this.params['required'] == 'y' && this.el_value == '') {
			this.add_error(0);
		} else if (this.params['required'] == 'y' || (this.params['required'] == 'n' && this.el_value != '')) {
			switch(this.params['type']) {
			case 'e' : //email
				this.check_email();
				break;
			case 'n' : //number
				this.check_number();
				break;
			case 'p' : //phone/fax
				this.check_phone();
				break;
			case 'd' : //date
				this.check_date();
				break;
			case 's' : //string
				this.check_string();
				break;
			case 'z' : //zipcode
				this.check_zipcode();
				break;
			case 'f' : //filename
				this.check_filename();
				break;
			}
		}
	}
	
	this.check_radio = function() {
		var i,checked=false;
		if (this.params['required'] == 'y') {
			for (i=0;i<this.form_obj[this.el_name].length;i++) {
				if (this.form_obj[this.el_name][i].checked) { checked = true; }
			}
			if (!checked) { this.add_error(6); }
		}
	}
	
	this.check_checkbox = function() {;
		if (this.params['required'] == 'y') {
			if (!this.form_obj[this.el_name].checked) { this.add_error(6); }
		}
	}
	
	this.check_select = function() {
		if (this.params['required'] == 'y') {
			if (this.el_value == 'null' || this.el_value == '') { this.add_error(7); }
		}
	}
	
	this.check_textarea = function() {
	
	}
	
	this.check_zipcode = function() {
		if (!this.zip_formats[this.params['format']].test(this.el_value)) { this.add_error(1); }
	}
	
	this.check_filename = function() {
		if (!this.file_formats[this.params['format']].test(this.el_value)) { this.add_error(10); }
	}
	
	this.check_date = function() {
		var i,tmp_array,sep_array=new Array('-','.','/'),sep;
		
		if (!this.date_formats[this.params['format']].test(this.el_value)) {
			this.add_error(1);
		} else {
			for (i=0;i<sep_array.length;i++) {
				if (this.el_value.indexOf(sep_array[i]) > -1) { sep = sep_array[i]; }
			}
			tmp_array = this.el_value.split(sep);
			
			switch (this.params['format']) {
			case '0' :
				if ((tmp_array[0]<1 || tmp_array[0]>12) || (tmp_array[1]<1 || tmp_array[1]>31)) { this.add_error(5); }
				break;
			case '1' :
				if ((tmp_array[0]<1 || tmp_array[0]>12) || (tmp_array[1]<1 || tmp_array[1]>31)) { this.add_error(5); }
				break;
			case '2' :
				if ((tmp_array[1]<1 || tmp_array[1]>12) || (tmp_array[2]<1 || tmp_array[2]>31)) { this.add_error(5); }
				break;
			}
		}
	}
	
	this.check_string = function() {
		if (this.string_formats[this.params['format']].test(this.el_value)) { this.add_error(1); }
		if (this.params['min_length'] != '' && this.el_value.length < this.params['min_length']) { this.add_error(4); }
		if (this.params['max_length'] != '' && this.el_value.length > this.params['max_length']) { this.add_error(9); }
	}
	
	this.check_email = function() {
		if (!this.email_formats[this.params['format']].test(this.el_value)) { this.add_error(1); }
	}
	
	this.check_number = function() {
		if (!this.number_formats[this.params['format']].test(this.el_value)) { this.add_error(2); }
		if (this.params['min_length'] != '' && this.el_value < this.params['min_length']) { this.add_error(3); }
		if (this.params['max_length'] != '' && this.el_value > this.params['max_length']) { this.add_error(8); }
	}
	
	this.check_phone = function() {
		if (!this.phone_formats[this.params['format']].test(this.el_value)) { this.add_error(1); }
	}
}

function close_alert() {
	document.getElementById('alert_div').style.display='none';
}