/**
 *  @class	KabAssertion
 *  @brief	Assertions for Form Validation (JavaScript)
 *  @author	<andre.steinitz@arbeitspakete.de>
 *
 *  @par Intent:
 *  
 *
 *  @par Usage:
 *
 *
 *
 *
 *
 *  @see KabFormValidator
 */


function KabAssertion(anElement)
    {

    this.MODE_CONJUNCTION = 1;
    this.MODE_DISJUNCTION = 2;
    this.MODE_IGNORE      = 3;

    
    this.onEvaluation = null;
    this.element      = anElement;
    this.description  = "";
    this.mode         = this.MODE_CONJUNCTION;
    this.isCorrect    = null;


    this.condition = function(aResult)
	{
	if (this.mode == this.MODE_IGNORE)
	    return this;
	
	if (null == this.isCorrect)
	    this.isCorrect = aResult;
	else
	    switch (this.mode)
		{
	        case this.MODE_CONJUNCTION:
	    	    this.isCorrect = (this.isCorrect && aResult);
		    break;

		case this.MODE_DISJUNCTION:
		    this.isCorrect = (this.isCorrect || aResult);
		    break;
		}
		
	return this;
	}


    this.evaluate = function()
	{
	if (this.onEvaluation)
	    this.onEvaluation(this);
	
	return this.isCorrect;
	}


    this.evaluateAndNotify = function(aCallbackFunction)
	{
	succ = this.evaluate();
	
	aCallbackFunction(this);
	
	return succ;
	}


    this.disjunction = function()
	{
	if (this.mode != this.MODE_IGNORE) 
	    this.mode = this.MODE_DISJUNCTION;
	    
	return this;
	}


    this.conjunction = function()
	{
	if (this.mode != this.MODE_IGNORE) 
	    this.mode = this.MODE_DISJUNCTION;
	    
	return this;
	}


    this.inWords = function(aDescription)
	{
	this.description = aDescription;

	return this;
	}
    

    /*
     *  hasLength(min, max)
     *  hasLength(0, 5)		between 0 and 5
     *  hasLength(5, 0)		5 or more characters
     *  hasLength(5, 5)		5 characters, not more not less
     *  hasLength(5)            short for hasLength(5, 5)
     */
    this.hasLength = function(aMinLength, aMaxLength)
	{
	valueLen = this.element.value.length;
	
	if (aMaxLength == null)
	    aMaxLength = aMinLenght;
	
	if (aMinLength <= aMaxLength)
	    hasLen = (valueLen >= aMinLength && valueLen <= aMaxLength);
	else
	    hasLen = (valueLen >= aMinLength);
	
	return this.condition(hasLen);
	}
	

    this.equals = function(anElementId)
	{
	emComparable = this.element.form.elements[anElementId];
	isEqual      = (this.element.value == emComparable.value);
	
	return this.condition(isEqual);
	}
	

    /*
     *  matches(/x/g, min, max)
     *  matches(/x/g, 0, 5)	between 0 and 5 hits, not more
     *  matches(/x/g, 3, 5)	between 3 and 5 hits, not more not less
     #  matches(/x/g, 5, 0)	5 or more hits, not less
     *  matches(/x/g, 5, 5)	5 hits, not more not less
     */
    this.matches = function(aRegEx, aMinNumber, aMaxNumber)
	{
	found = (aMinNumber == 0);
	hits  = this.element.value.match(aRegEx);

	if (hits)
	    {
	    if (aMinNumber <= aMaxNumber)
		found = (hits.length >= aMinNumber && hits.length <= aMaxNumber);
	    else
		found = (hits.length >= aMinNumber);
	    }

	return this.condition(found);
	}
    

    this.notEmpty = function()
	{
	return this.hasLength(1, 0);
	}
    

    this.notEmptyIf = function(aCondition)
	{
	if (aCondition == true)
	    return this.notEmpty();
	else
	    return this.condition(true); // isCorrect undefined otherwise
	}


    this.isEmpty = function()
	{
	return this.hasLength(0, 0);
	}


    this.isNumeric = function()
	{
	value = this.element.value.replace(/[\,\.]/, "x"); // no floats
	isNum = (!isNaN(value));
	return this.condition(isNum);
	}


    /* 
     *  if argument is FALSE ignore following constraints otherwise continue
     *  .ifThen(document.frmX.cbX.checked).notEmpty().isNumeric().evaluate()
     *
     */
    this.ifThen = function(aResult)
	{
	if (aResult == false)
	    {
	    this.condition(true);	    
	    this.mode = this.MODE_IGNORE;
	    }
	else
	    this.mode = this.MODE_CONJUNCTION;
	
	return this;
	}


    this.hasEmailFormat = function()
	{
	return this.matches(/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/g, 1, 1);
	}


    commands = this.element.getAttribute("assert");

    if (commands) eval("this." + commands); 

    }



/**
 *  @class 	KabFormValidator
 *  @brief	Form Validation (JavaScript)
 *  @author	<andre.steinitz@arbeitspakete.de>
 *
 *  @par Intent:
 *  
 *
 *  @par Usage:
 *
 *  Example assertion handler:
 *  @code
 *  function noticeEvaluationOf(anAssertion) 
 *  	{ 
 *	if (anAssertion.isCorrect)
 *	    anAssertion.element.XXX; // mark element valid
 *	else
 *	    anAssertion.element.YYY; // mark element invalid
 *	}
 *  @endcode
 *
 *  Create new validator:
 *  @code
 *  form = document.forms["frmExampleForm"];
 *  validation = new KabFormValidator(form);
 *  validation.assertionHandler = noticeEvaluationOf;
 *  @endcode
 *
 *  Evaluate assertion for an element:
 *  @code
 *  validation.assert("myField").notEmpty().hasLength(6, 0).equals("anotherField").matches(/x/g, 0, 1).evaluate();
 *  @endcode
 *
 *  Define assertion with HTML-attribute (+ optional trigger onkeyup):
 *  @code
 *  <input name="textField"
 *      id="textField"
 *      type="text"
 *      assert="isNumeric().hasLength(7, 0).inWords('7 or more digits expected')"
 *      onkeyup="validation.assert('textField').evaluate()" />
 *  @endcode
 *
 *  Evaluate assertion for an HTML-element with assert-attribute:
 *  @code
 *  validation.assert("textField").evaluate();
 *  @endcode
 *
 *  Disjunction:
 *  @code
 *  assertion = validation.assert("textfield").disjunction().isEmpty().hasLength(7, 0);
 *  assertion.inWords('value is empty or has more than 6 chars');
 *  assertion.evaluate();
 *  @endcode
 *
 *  Are all assertions for a form correct?
 *  @code
 *  if (!validation.evaluateAll()) alert("There are errors.");
 *  @endcode
 *
 *  If not, which assertions failed?
 *  @code
 *  errors = validation.errors;
 *  ...
 *  @endcode
 *
 *
 *  @see KabAssertion
 *    
 */

function KabFormValidator(aForm)
    {

    this.assertionHandler = null;
    this.form             = aForm;
    this.errors           = new Array();


    /* factory methods */
    
    this.assertEm = function(element)
	{
	assertion = new KabAssertion(element);
	
	assertion.onEvaluation = this.assertionHandler;

	return assertion;
	}
	
    
    this.assert = function(anElementId) 
	{
	element = this.form.elements[anElementId];
	return this.assertEm(element);
	}



    /* evaluation */

    this.assertAll = function(canEvaluate)
	{
	this.errors = [];
	
	elements = this.form.elements;
	
	for (i = 0; i < elements.length; i++)
	    {
	    element   = elements[i];
	    attribute = element.getAttribute("assert");

	    if (attribute)
		{
		assertion = this.assertEm(element);
		
		if (canEvaluate)
		    assertion.evaluate();

		if (!assertion.isCorrect)
		    this.errors.push(assertion);
		}
	    }
	    
	return (this.errors.length == 0);
	}


    this.evaluateAll = function()
	{
	return this.assertAll(true);
	}

    }
    
function test()
	{
	alert('sdfsdf');
	}
