/* $Id: validator.js,v 1.2 2004/10/02 16:04:28 kwiateek Exp $ */

/**
 * Project:		webEngine
 * File:		validator.js
 * Date:		2004-09-25
 * Description:		Validator (JavaScript extension)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.
 *
 * This program 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 * USA.
 *
 * @link http://kwiateek.net/projects/webEngine/
 * @copyright 2004 kwiateek.net
 * @author Piotr Kwiatkowski <webmaster@kwiateek.net>
 * @package webEngine
 */
 
// Name of CSS class when error accured
var errorClass = 'error';

// Fields to check
var fieldsToCheck = new Array();

// Define a variable to hold an error message.
var errorMessages = String("");

// Added DOM detection for Opera which doesn't handle the length property of
// an array of form elements.
isDOM = document.getElementById ? 1 : 0; 

/**
 * Removing spaces from data.
 * This function is helpful if somebody want to cheat validator
 */
function stripSpaces(data) 
{
	while (data.substring(0,1) == ' ') {
		data = data.substring(1);
	}
	return data;
}

/**
 * Checking if is empty
 */
function empty(string) 
{
	if (string.length > 0) {
		return false;
	}
	return true; 
}

/**
 * Check if specified needle is in Array
 */
function isInArray(needle, arrayStack)
{
	for (i = 0; i < arrayStack.length; i++) {
		if (arrayStack[i] == needle) {
			return true;
		}
	}
	return false;
}

/**
 * Set error CSS class for specified input
 */
function setCSSClass(form, input)
{
	form.elements[input].className = errorClass;
}

/**
 * Append error message to errorMessages stack
 */
function appendError(message)
{
	errorMessages += message + "\n";
}

/**
 * Checking if field is not blank
 */
function isNonBlank(form, field, message) 
{
	var element = form.elements[field];

	if (!empty(stripSpaces(element.value))) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field);

	// Append eror
	appendError(message);
	return false;
}

/**
 * Checking if max length in field have been overturned
 */
function isMaxLength(form, field, arguments) 
{
	var element = form.elements[field];
	var message = arguments[0];
	var maxLength = arguments[1];

	if (element.value.length <= maxLength || empty(element.value)) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field);

	// Append eror
	appendError(message);
	return false;
}

/**
 * Checking if min length in field have been raised
 */
function isMinLength(form, field, arguments) 
{
	var element = form.elements[field];
	var message = arguments[0];
	var minLength = arguments[1];

	if (element.value.length >= minLength || empty(element.value)) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field);

	// Append eror
	appendError(message);
	return false;
}

/**
 * Checking if value of field1 is similar to field2
 */
function isSimilar(form, field1, arguments) 
{
	var field2 = arguments[1];
	var element1 = form.elements[field1];
	var element2 = form.elements[field2];
	var message = arguments[0];

	if (element1.value == element2.value || empty(element1.value)) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field1);

	// Append eror
	appendError(message);
	return false;
}

/**
 * Checking if option is selected
 */ 
function isSelected(form, field, message) 
{
	var element = form.elements[field]

	if (!element.selectedIndex == 0) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field);

	// Append eror
	appendError(message);
	return false;
}

/**
 * Checking if data is numeric
 */ 
function isNumeric(form, field, message) 
{
	var element = form.elements[field];

	if (!isNaN(parseInt(element.value)) || empty(element.value)) {
		return true;
	}
	// Append eror
	appendError(message);
	return false;
}

/**
 * Checking if radio or checkbox is checked
 */ 
function isChecked(form, field, message) 
{
	var element;
	var j = 0; // This is so that we know which RADIO/CHECKBOX element to return to

	if(isDOM) {
        	element = document.getElementsByTagName('INPUT'); // returns a Node List
		for (var i = 0; i <= element.length-1; ++i) {
			if (element[i].name == field) {
				if (j == 0) {
					j = i;
				}
				if (element[i].checked) {
					return true; // one is checked after all!
				}
			}
        	}
	} else {
		element = form.elements[field] ; // returns an array of elements
		for (var i = 0; i < element.length; ++i) {
			if (element[i].checked) { 
				return true;  // one is checked after all!
				break;
			} 
		}
	}
	// Append eror
	appendError(message);
	return false; 
}

/**
 * Main validation date function requested by isEmail()
 * Reference: Sandeep V. Tamhankar (stamhankar@hotmail.com),
 * http://javascript.internet.com
 */
function validEmail(emailStr) {
	if (emailStr.length == 0) {
		return true;
	}
	var emailPat=/^(.+)@(.+)$/;
	var specialChars="\\(\\)<>@,;:\\\\\\\"\\.\\[\\]";
	var validChars="\[^\\s" + specialChars + "\]";
	var quotedUser="(\"[^\"]*\")";
	var ipDomainPat=/^(\d{1,3})[.](\d{1,3})[.](\d{1,3})[.](\d{1,3})$/;
	var atom=validChars + '+';
	var word="(" + atom + "|" + quotedUser + ")";
	var userPat=new RegExp("^" + word + "(\\." + word + ")*$");
	var domainPat=new RegExp("^" + atom + "(\\." + atom + ")*$");
	var matchArray=emailStr.match(emailPat);
	if (matchArray == null) {
		return false;
	}
	var user=matchArray[1];
	var domain=matchArray[2];
	if (user.match(userPat) == null) {
		return false;
	}
	var IPArray = domain.match(ipDomainPat);
	if (IPArray != null) {
		for (var i = 1; i <= 4; i++) {
			if (IPArray[i] > 255) {
				return false;
			}
		}
		return true;
	}
	var domainArray=domain.match(domainPat);
	if (domainArray == null) {
		return false;
	}
	var atomPat=new RegExp(atom,"g");
	var domArr=domain.match(atomPat);
	var len=domArr.length;
	if ((domArr[domArr.length-1].length < 2) ||
		(domArr[domArr.length-1].length > 3)) {
		return false;
	}
	if (len < 2) {
		return false;
	}
	return true;
}

/**
 * Checking if email is valid
 */ 
function isEmail(form, field, message) 
{
	var element = form.elements[field];

	if (validEmail(element.value) || empty(element.value)) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field);

	// Append eror
	appendError(message);
	return false;
}


/**
 * Checking if date is valid
 * Date can be passed to this function in to methods
 * 1. Passing date string in one part
 *    for example: 
 *    isDate(form, dateString, Array(errorMessage, minYear, maxYear)) 
 *    where dateString is in MM/DD/YYYY format
 * 2. Passing each part of date 
 *    for example: 
 *    isDate(form, month, Array(errorMessage, day, year, minYear, maxYear)) 
 */
function isDate(form, field, arguments) 
{
	if (arguments.length == 5) {
		// Prepare rest of required variables
		var fieldDay = arguments[1];
		var fieldMonth = field;
		var fieldYear = arguments[2];
	
		// Append values of fields
		var elementDay = form.elements[fieldDay];
		var elementMonth = form.elements[fieldMonth];
		var elementYear = form.elements[fieldYear];

		// Prepare date string (format MM/DD/YYYY)
		var dateString = elementMonth.value + '/' + elementDay.value + '/' + elementYear.value;

		// Get min and max year number
		var minYear = arguments[3];
		var maxYear = arguments[4];
	} else {
		var element = form.elements[field];
		var dateString = element.value;

		// Get min and max year number
		var minYear = arguments[1];
		var maxYear = arguments[2];
	}

	// Error message
	var message = arguments[0];

	if (validDate(dateString, minYear, maxYear) || dateString == '0/0/0') {
		return true;
	}

	// Colorize inputs
	if (arguments.length == 5) {
		setCSSClass(form, fieldDay);
		setCSSClass(form, fieldMonth);
		setCSSClass(form, fieldYear);
	} else {
		setCSSClass(form, field);
	}

	// Append eror
	appendError(message);
	return false;
}

/**
 * Main validation date function requested by isDate()
 * Based on function download from http://www.breakingpar.com
 */
function validDate(dateString, minYear, maxYear)
{
	// Date format
	var format = 'MDY';

	var reg1 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{2}$/;
	var reg2 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;

	// If it doesn't conform to the right format (with either a 2 digit year or 4 digit year), fail
	if ( (reg1.test(dateString) == false) && (reg2.test(dateString) == false) ) { 
		return false; 
	}

	// Split into 3 parts based on what the divider was
	var parts = dateString.split(RegExp.$1);

	// Check to see if the 3 parts end up making a valid date
	if (format.substring(0, 1) == "M") { 
		var mm = parts[0]; 
	} else if (format.substring(1, 2) == "M") { 
		var mm = parts[1]; 
	} else { 
		var mm = parts[2]; 
	}
	if (format.substring(0, 1) == "D") { 
		var dd = parts[0]; 
	} else if (format.substring(1, 2) == "D") { 
		var dd = parts[1]; 
	} else { 
		var dd = parts[2]; 
	}
	if (format.substring(0, 1) == "Y") { 
		var yy = parts[0]; 
	} else if (format.substring(1, 2) == "Y") { 
		var yy = parts[1]; 
	} else { 
		var yy = parts[2]; 
	}
	if (parseFloat(yy) <= 50) { 
		yy = (parseFloat(yy) + 2000).toString(); 
	}
	if (parseFloat(yy) <= 99) { 
		yy = (parseFloat(yy) + 1900).toString(); 
	}

	// Check wheather is in valid interval (minYear / maxYear)
	if ( (minYear > 0 && minYear > yy) ||
	     (maxYear > 0 && maxYear < yy)) {
		return false;
	}

	// A new date object is created in JavaScript. If the numbers are out of range 
	// (like February 31), JavaScript will still create an object, it will just be 
	// adjusted to be a valid date. So create the date and then check to see if it was 
	// adjusted by JavaScript. If it was adjusted, then the original date is not a valid date.
	var dt = new Date(parseFloat(yy), parseFloat(mm)-1, parseFloat(dd), 0, 0, 0, 0);

	if (parseFloat(dd) != dt.getDate()) { 
		return false; 
	}
	if (parseFloat(mm)-1 != dt.getMonth()) { 
		return false; 
	}
	return true;
}

/**
 * Validator initializator
 */
function validate(form)
{
	var reportedFields = Array(); // Fields in which error accured
	var formName = fieldsToCheck[0][0];
	var formErrorMessage = fieldsToCheck[0][1];

	if (formName == form.name) {
		errorMessages += formErrorMessage + "\n\n"; // Set form error header

		for (var i = 1; i < fieldsToCheck.length; ++i) {
			if (!isInArray(fieldsToCheck[i][0], reportedFields)) {
				if (!(fieldsToCheck[i][1](form, fieldsToCheck[i][0], fieldsToCheck[i][2]))) {
					reportedFields.push(fieldsToCheck[i][0]);
				}
			}
		}
		if (errorMessages.length >= 1) {
			element = form.elements[reportedFields[0]];
			element.focus(); // Mark the first field where error accured
			alert(errorMessages);
			errorMessages = ""; // Free results
			return false;
		} else {
			return true;
		}
	}
}