/* $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; 

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

/**
 * 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;
}

function nullCSSClass(form, input)
{
	form.elements[input].className = '';
}

/**
 * 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;
}
function isNonBlankReq(form, field, arguments)
{
	var message = arguments[0];
	var reqInput = arguments[1];

	var element = form.elements[field];
	var reqInputElement = document.getElementById(reqInput);

	if (!reqInputElement.checked) {
		return true;
	}

	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 alphanumeric
 */
function isAlphaNumeric(form, field, message) 
{
	var element = form.elements[field];
	var ValidChars = "abcdefghijklmnoprstuwxyzqvABCDEFGHIJKLMNOPRSTUWXYZQV1234567890._-";
	var IsAlpaNumeric = true;
	var Char;

	if (empty(element.value)) {
		return true;
	}
	for (i = 0; i < element.value.length && IsAlpaNumeric == true; i++) {
		Char = element.value.charAt(i);
		if (ValidChars.indexOf(Char) == -1) {
			IsAlpaNumeric = false;
		}
	}
	if (IsAlpaNumeric) {
		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];
	var ValidChars = "0123456789";
	var IsNumber = true;
	var Char;

	if (empty(element.value)) {
		return true;
	}
	for (i = 0; i < element.value.length && IsNumber == true; i++) {
		Char = element.value.charAt(i);
		if (ValidChars.indexOf(Char) == -1) {
			IsNumber = false;
		}
	}
	if (IsNumber) {
		return true;
	}

	// Colorize inputs
	setCSSClass(form, field);

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

/**
 * Checking if radio or checkbox is checked
 */ 
function isChecked(form, field, arguments)
{
	var message = arguments[0];
	var type = arguments[1];

	switch(type)
	{
		case 'simple':
			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;
					} 
				}
			}
		break;

		case 'multi':
			return true;
		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 postcode is valid
 */ 
function isPostcode(form, field, message) 
{
	var element = form.elements[field];

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

	// Colorize inputs
	setCSSClass(form, field);

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

/**
 * Main validation postcode function requested by isPostcode()
 */
function validPostcode(postcode)
{
        if (postcode.match(/^[0-9]{2}-[0-9]{3}$/)) {
		return true;
	}
	return false;
}

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

	//if (validUrl(element.value) || empty(element.value) || element.value == 'http://') {
		return true;
	//}

	// Colorize inputs
	setCSSClass(form, field);

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

/**
 * Main url validate function requested by isUrl()
 */
function validUrl(url)
{
        if (url.match(/^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}'.'((:[0-9]{1,5})?\/.*)?$/)) {
		return true;
	}
	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 = field;
		var fieldMonth = arguments[1];
		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)) {
		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;
}

var debugWindow = null;

function debug(s, name) {
    if (!debugWindow) {
        debugWindow = window.open("", "debugWindow", "width=400,height=500,scrollbars=yes,resizable=yes");
        debugWindow.document.write("<pre>");
    }
    if (name) {
        debugWindow.document.write('<div style="font: 12px sans-serif; font-weight: bold;">'+name+'</div>');
    }
    debugWindow.document.write(s + "\n");
}

function debugObject(obj, name) {
    var s = '';
    for (var i in obj) {
        if (obj[i] && (typeof obj[i] == "object" || typeof obj[i] == "function") && obj[i].toString) {
            s += "Object." + i + "=" + obj[i].toString().replace(/\n/g, "") + "\n";
        } else {
            s += "Object." + i + "=" + obj[i] + "\n";
        }
    }
    debug(s, name);
}

function debugArray(arr, name) {
    var s = '';
    for (var i = 0; i < arr.length; ++i) {
        s += "Array[" + i + "]=" + arr[i] + "\n";
    }
    debug(s, name);
}

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

	if (formName == form.name) {
		for (var i = 1; i < fieldsToCheck[form.name].length; ++i) {
			if (!isInArray(fieldsToCheck[form.name][i][0], reportedFields)) {
				if (!(fieldsToCheck[form.name][i][1](form, fieldsToCheck[form.name][i][0], fieldsToCheck[form.name][i][2]))) {
					reportedFields.push(fieldsToCheck[form.name][i][0]);
				} else {
					nullCSSClass(form, fieldsToCheck[form.name][i][0]);
				}
			}
		}

		if (errorMessages.length >= 1) {
			element = form.elements[reportedFields[0]];
			// if (element.type != 'hidden' && element.type != 'checkbox') {
			//element.focus(); // Mark the first field where error accured
			// }
			alert(formErrorMessage + "\n\n" + errorMessages);
			errorMessages = ""; // Free results
			return false;
		} else {
			return true;
		}
	}
}
