/* FILE HEADER **************************************************
** JS Validate
** Author: Karl Seguin, Timo Haberkern
** Homepage: http://jsval.berlios.de/
** Version: 0.5.1
** Copyright 2003, 2004 Karl Seguin
	This file is part of JS Validate.
	JS Validate is free software; you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.
	
	JS Validate is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with JS Validate; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**
** END HEADER ***************************************************/

function validateCompleteForm (objForm) {
    return _validateInternal(objForm, 0);
}

function validateStandard (objForm) {
    return _validateInternal(objForm, 1);
}

function validate_one(element) {
	if (!element.form.getLabelsDone) getLabels(element.form);

	field = new Field(element);

	if (field.value=='') field.SetStatus('');
	else if (!field.IsValid()) field.SetStatus('error');
	else { field.ResetClass(); }
	
	/*if (element.equalto) {	//check equal field
		field = new Field(element.form[element.equalto]);
		field.ResetClass();
		if (!field.IsValid()) field.SetStatus('error');
	}*/
}

/***************************************************************
** Internal functions
*****************************************************************/
function _validateInternal(form, nErrorThrowType){
	var strErrorMessage = ""; var objFirstError = null;
	if (!form.getLabelsDone) getLabels(form);
	if (nErrorThrowType == 0) {
		strErrorMessage = (form.err) ? form.err : _getLanguageText("err_form");
	}

	var fields = _GenerateFormFields(form);
	for (var i = 0; i < fields.length; ++i){
		var field = fields[i];
		field.ResetClass();
		if (!field.IsValid()){
			field.SetStatus('error');
			if (nErrorThrowType == 1) {
				_throwError(field);
				return false;
		    }else{
				if (objFirstError == null) objFirstError = field;
				strErrorMessage = _handleError (field, strErrorMessage);
				bError = true;
		    }
		}
	}
	if (objFirstError != null) {
		alert(strErrorMessage);
		objFirstError.element.focus();
		return false;
	}
  return true;
}

function _getLanguageText(id){
	objTextsInternal = new _jsVal_Language();
	objTexts = null;
	try {
		objTexts = new jsVal_Language();
	} catch (ignored){};
	switch (id) {
		case "err_form": strResult = (!objTexts || !objTexts.err_form) ? objTextsInternal.err_form : objTexts.err_form; break;
		case "err_enter": strResult = (!objTexts || !objTexts.err_enter) ? objTextsInternal.err_enter : objTexts.err_enter; break;
		case "err_select": strResult = (!objTexts || !objTexts.err_select) ? objTextsInternal.err_select : objTexts.err_select; break;
	}
  return strResult;
}

function _GenerateFormFields(form){
   var arr = new Array();
	for (var i = 0; i < form.length; ++i){
		var element = form.elements[i];
		var index = _getElementIndex(arr,element);
		//if it doesn't already exist, add it to our array, else merge the change
		if (index) {
			if (index == -1) arr[arr.length] = new Field(element);
			else arr[index].Merge(element);
		}
	}
	return arr;
}

function _getElementIndex(arr, element){
	if (!element.name) return;
	var elementName = element.name.toLowerCase();
	for (var i = 0; i < arr.length; ++i){
		if (arr[i].element.name.toLowerCase() == elementName) return i;
	}
	return -1;
}

/***************************************************************
** Standard translation
*****************************************************************/
function _jsVal_Language() {
	//this.err_form = "Please enter/select values for the following fields:\n\n";
	this.err_form = "אנא מלא/תקן את שדות הבאים:\n\n";
	this.err_select = "Please select a valid \"%FIELDNAME%\"";
	//this.err_enter = "Please enter a valid \"%FIELDNAME%\"";
	this.err_enter = "נא להזין שדה: %FIELDNAME%";
}

/***************************************************************
** Field Class
*****************************************************************/
function Field(element){
	this.name = element.name;
	this.type = element.type;
	this.form = element.form;
	this.label = element.label;
	this.element = element;
	this.exclude = element.exclude || element.getAttribute('exclude');
	this.err = element.err || element.getAttribute('err');
	this.required = _parseBoolean(element.required || element.getAttribute('required'));
	this.realname = element.realname || element.getAttribute('realname');
	if (document.getElementById) this.statusImg=document.getElementById(this.name+'_status');

	switch (this.type){
		case "textarea":
		case "password":
		case "text":
			this.value = element.value;
			this.equalto = element.equalto || element.getAttribute('equalto');
			this.minLength = element.minlength || element.getAttribute('minlength');
			this.maxLength = element.maxlength || element.getAttribute('maxlength');
			this.regexp = this._getRegEx(element);
			this.minValue = element.minvalue || element.getAttribute('minvalue');
			this.maxValue = element.maxvalue || element.getAttribute('maxvalue');
			break;
	  case "select-one":
	  case "select-multiple":
	     this.values = new Array();
	     for (var i = 0; i < element.options.length; ++i){
	        if (element.options[i].selected && (!this.exclude || element.options[i].value != this.exclude)){
			   this.values[this.values.length] = element.options[i].value;
	        }
	     }
	     this.min = element.min || element.getAttribute('min');
	     this.max = element.max || element.getAttribute('max');
	     break;
	  case "checkbox":
	     this.min = element.min || element.getAttribute('min');
	     this.max = element.max || element.getAttribute('max');
	     //no break, let it fall through to radio
	  case "radio":
	      this.required = _parseBoolean(this.required || element.getAttribute('required'));
	      this.values = new Array();
	      if (element.checked){
	         this.values[0] = element.value;
	      }
	      this.elements = new Array();
	      this.elements[0] = element;
	      break;
	}
}

Field.prototype.Merge = function(element){
	//never negate a require field
	var required = _parseBoolean(element.getAttribute('required'));
	if (required) this.required = true;
	
	//all other cases (except required) we only add if there isn't already a value (first come first served)
	if (!this.err) this.err = element.getAttribute('err');
	if (!this.equalto) this.equalto = element.getAttribute('equalto');
	if (!this.realname) this.realname = element.getAttribute('realname');
	if (!this.max) this.max = element.getAttribute('max');
	if (!this.min) this.min = element.getAttribute('min');
	if (!this.regexp) this.regexp = this._getRegEx(element);
	if (element.checked) this.values[this.values.length] = element.value;
	
	this.elements[this.elements.length] = element;
}

Field.prototype.IsValid = function(){
	switch (this.type){
		case "textarea":
		case "password":
		case "text":
			return this._ValidateText();
		case "select-one":
		case "select-multiple":
		case "radio":
		case "checkbox":
			return this._ValidateGroup();
		default:
			return true;
	}
}

Field.prototype.SetStatus = function(Status) {
	if (Status=='error') {
		if (this.elements) for (var i=0; i<this.elements.length; ++i) this.removeClassName(this.elements[i], 'error');
		else this.addClassName(this.element, 'error');
		if (this.label) this.addClassName(this.label, 'error');
	}else {
		this.ResetClass();
	}
}


Field.prototype.hasClassName = function(element, className) {
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
}
Field.prototype.addClassName = function(element, className) {
	if (!this.hasClassName(element, className)) element.className += (element.className ? ' ' : '') + className;
}
Field.prototype.removeClassName = function(element, className) {
    element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').replace(/^\s+/, '').replace(/\s+$/, '');
}


Field.prototype.ResetClass = function(){
	if (this.elements) for (var i=0; i<this.elements.length; ++i) this.removeClassName(this.elements[i], 'error');
	else this.removeClassName(this.element, 'error');
	if (this.label) this.removeClassName(this.label, 'error');
}

Field.prototype._getRegEx = function(element){
	regex = element.regexp || element.getAttribute('regexp')
	if (regex == null) return null;
	retype = typeof(regex);
	if (retype.toUpperCase() == "FUNCTION")
		return regex;
	else if ( (retype.toUpperCase() == "STRING") && !(regex=="email") && !(regex=="date") && !(regex=="tel")
		&& !(regex=="pc") && !(regex=="zip") && !(regex=="money") 
		&& !(regex=="creditcard") && !(regex=="postalzip"))
	{
	   nBegin = 0; nEnd = regex.length;
	   if (regex.charAt(0) == "/") nBegin=1;
	   if (regex.charAt(regex.length-1) == "/") nEnd=regex.length-1;
	   return new RegExp(regex.slice(nBegin, nEnd));
	}
	else {
		return regex;
	}
}

Field.prototype._ValidateText = function(){
	//check if equal to equalto
	if (this.equalto && this.form[this.equalto]){
		if (this.value != this.form[this.equalto].value) return false;
	}
	//required value is empty
	if (this.required && !trim(this.value)){
		return false;
	}
	//value less than minlength
	if (this.value && (this.minLength && trim(this.value).length < this.minLength)){
		return false;
	}
	//value is more than maxlength
	if (this.value && (this.maxLength && trim(this.value).length > this.maxLength)){
		return false;
	}
	//value fails regular expression
	if (this.regexp && !_checkRegExp(this.regexp, this.value)){
		//the field isn't required, but there is a value
		if (!this.required && this.value) return false;
		if (this.required) return false;
	}
	//check against minvalue and maxvalue
	if (this.required){
		var fValue = parseFloat(this.value);
		if ((this.minValue || this.maxValue) && isNaN(fValue)) return false;
		if (this.minValue && fValue < this.minValue) return false;
		if (this.maxValue && fValue > this.maxValue) return false
	}
	return true;
}

Field.prototype._ValidateGroup = function(){
	if (this.required && (this.values=='' || this.values.length==0)) return false;
	if (this.required && this.min && this.min > this.values.length) return false;
	if (this.required && this.max && this.max < this.values.length) return false;
	return true;
}



function trim(str) {
	return str.replace(/^\s*|\s*$/g,"");
}

function _handleError (field, strErrorMessage) {
	var obj = field.element;
	strNewMessage = strErrorMessage + ( (field.realname)? field.realname : ((obj.id) ? obj.id : obj.name) ) + "\n";
	return strNewMessage;
}

function _throwError(field){
   var obj = field.element;
   switch (field.type){
      case "text":
      case "password":
      case "textarea":
         alert(_getError(field, "err_enter"));
         obj.focus();
         break;
      case "select-one":
      case "select-multiple":
      case "radio":
      case "checkbox":
         alert(_getError(field, "err_select"));
         break;
      }
}

function _getError(field, str){
   var obj = field.element;
   strErrorTemp = (field.err) ? field.err : _getLanguageText(str);
   return strErrorTemp.replace("%FIELDNAME%", (field.realname)? field.realname : ((obj.id) ? obj.id : obj.name));
}

function _parseBoolean(value){
   return !(!value || value == 0 || value == "0" || value == "false");
}

function _checkRegExp(regx, value){
  switch (regx){
	case "email":	return ((/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,5})+$/).test(value));
	case "date":	return dateCheck(value);
	case "tel":		return ((/^1?[\- ]?\(?\d{3}\)?[\- ]?\d{3}[\- ]?\d{4}$/).test(value));
	case "pc":		return ((/^[a-z]\d[a-z] ?\d[a-z]\d$/i).test(value));
	case "zip":		return ((/^\d{5}$/).test(value));
	case "money":	return ((/^\d+([\.]\d\d)?$/).test(value));
	case "creditcard": return (!isNaN(value));
	case "postalzip":
		if(value.length==6 || value.length==7) return((/^[a-zA-Z]\d[a-zA-Z] ?\d[a-zA-Z]\d$/).test(value));
		if(value.length==5 || value.length==10) return((/^\d{5}(\-\d{4})?$/).test(value));
	break;
	default:
	return (regx.test(value));
  }
}

function dateCheck(str){
	year=month=day=hour=minute=false;
	var re = new RegExp("^([0-9]{1,2})[./]([0-9]{1,2})[./]([0-9]{2,4})( ([0-9]{1,2}):([0-9]{1,2}))?$");
	if ( (ar=re.exec(str))!=null ) {
		if (ar[3].length==2) ar[3]='20'+ar[3];
		year=ar[3]; month=ar[2]; day=ar[1]; hour=ar[5]; minute=ar[6];
	}else {
		var re = new RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})( ([0-9]{1,2}):([0-9]{1,2}))?$");
		if ((ar=re.exec(str))!=null) {
			year=ar[1]; month=ar[2]; day=ar[3]; hour=ar[5]; minute=ar[6];
		}
	}
	
	if (!year || !month || !day) return false;
	if ( hour && (hour>23 || minute>59) ) return false;
	
	var date1 = new Date(year, Number(month-1), day);
	if ( Number(month-1) != date1.getMonth() ) return false;
	return true;
}

function getLabels(form) {
	if (form.getLabelsDone) return;
	if(!document.getElementsByTagName) return;
	
	// set labels
	var labelList = form.getElementsByTagName('label');
	for(var lbl=0; lbl<labelList.length; lbl++) {	// loop through label array attempting to match each 'for' attribute to the id of the current element
		// Internet Explorer requires the htmlFor test
		if(labelList[lbl]['htmlFor']) label=labelList[lbl]['htmlFor']
		// All other compliant browsers
		else if(labelList[lbl].getAttribute('for')) label = labelList[lbl].getAttribute('for');
		else label=false;	// label not found

		if (label && form[label]) form[label].label=labelList[lbl];
	}
	form.getLabelsDone=true;
}
