/*	name			: ClassBehaviours, the javascript framework based on class-name parsing	update			: 9.3.17	author			: Maurice van Creij	dependencies	: jquery.classbehaviours.js	info			: http://www.classbehaviours.com/

    This file is part of jQuery.classBehaviours.
    
    ClassBehaviours is a javascript framework based on class-name parsing.
    Copyright (C) 2008  Maurice van Creij

    ClassBehaviours is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ClassBehaviours 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 ClassBehaviours. If not, see http://www.gnu.org/licenses/gpl.html.*/

	// create the jQuery object if it doesn't already exist
	if(typeof(jQuery)=='undefined') jQuery = function(){};
	
	// create the root classbehaviours object if it doesn't already exist
	if(typeof(jQuery.classBehaviours)=='undefined') jQuery.classBehaviours = function(){};
	
	// create the handlers child object if it doesn't already exist
	if(typeof(jQuery.classBehaviours.handlers)=='undefined') jQuery.classBehaviours.handlers = function(){}

	// blinks
	jQuery.classBehaviours.handlers.validateForm = {
		// properties
		name: 'validateForm',
		focused: null,
		// methods
		start: function(node){
			// find the root of the form and set its onsubmit event
			formNode = jQuery.classBehaviours.utilities.rootNode(node, 'FORM');
			formNode.onsubmit = this.submitted;
			// for all elements of this form
			allNodes = node.getElementsByTagName('*');
			for(var a=0; a<allNodes.length; a++){
				// if this element is a row add the default class name
				if(allNodes[a].nodeName=='TR') allNodes[a].className += (allNodes[a].className.indexOf('passive')<0) ? ' passive' : '' ;
				// if this element is a submit button, hijack the form submit
				if(allNodes[a].type=='submit' || allNodes[a].type=='image') allNodes[a].onclick  = this.submitted;
				// if this element is marked with the test_* class
				if(allNodes[a].className.indexOf('test_')>-1){
					// set the default validation class
					allNodes[a].className += (allNodes[a].className.indexOf('passive')<0) ? ' passive' : '' ;
					// test properties
					testName = jQuery.classBehaviours.utilities.getClassParameter(allNodes[a], 'test', null);
					testApplied = null;
					// for all tests, check if this is the name and make it apply itsself to the form element
					for(b in this.tests) if(this.tests[b].name == testName) testApplied = this.tests[b].apply(allNodes[a]);
					// if it wasn't found assume it's the id to a regExp, apply the custom regexp handler
					if(testApplied==null) testApplied = this.custom.apply(allNodes[a]);
				}
			}
		},
		summary: function(node){
			// get all elements of this form
			rootNode = jQuery.classBehaviours.utilities.rootNode(node, 'FIELDSET');
			allRows = rootNode.getElementsByTagName('TR');
			// for all rows, if the row is an error, remember its label
			errorMessage = '';
			for(var a=0; a<allRows.length; a++){
				if(allRows[a].className.indexOf('error')>-1 && allRows[a].innerHTML.indexOf(' test_')>-1){
					errorLabels = allRows[a].getElementsByTagName('LABEL');
					if(errorLabels.length>0) errorMessage += '<li>'+ errorLabels[0].innerHTML +'</li>';
				}
			}
			// get the summary field
			validationSummaries = jQuery.classBehaviours.utilities.getElementsByClassName('summary', rootNode);
			if(validationSummaries.length>0){
				// construct the summary message
				//validationTextId = jQuery.classBehaviours.utilities.getClassParameter(validationSummaries[0], 'failure', '');
				//validationText = document.getElementById(validationTextId).value;
				//validationSummaries[0].innerHTML = validationText.replace('{labels}', '<ul>' + errorMessage + '</ul>');
				//validationSummaries[0].style.display = (errorMessage!='') ? 'block' : 'none' ;
			}
			// return if the form passed the tests
			return (errorMessage=='');
		},
		update: function(node, status){
			// is this a mandatory field
			mandatoryId = jQuery.classBehaviours.utilities.getClassParameter(node, 'required', 'no');
			mandatory = (mandatoryId!='yes' && mandatoryId!='no') ? document.getElementById(mandatoryId).checked : (mandatoryId=='yes');
			// is this an empty field
			empty = (node.value == '' || node.value == node.title);
			// adjust the status for non-mandatory empty field
			ignore = (!mandatory && empty);
			// adjust the classname of the form element to reflect the validation status
			node.className = (ignore) ? 
				node.className.replace('success', 'passive').replace('error', 'passive') : 
				(status) ? 
					node.className.replace('passive', 'success').replace('error', 'success') : 
					node.className.replace('passive', 'error').replace('success', 'error') ;
			// get the root of this row
			rootNode = jQuery.classBehaviours.utilities.rootNode(node, 'TR');
			// adjust the classname of the form row to reflect the validation status
			rootNode.className = (ignore) ? 
				rootNode.className.replace('success', 'passive').replace('error', 'passive') : 
				(status) ? 
					rootNode.className.replace('passive', 'success').replace('error', 'success') : 
					rootNode.className.replace('passive', 'error').replace('success', 'error') ;
			// pass back the status
			return (status || ignore);
		},
		ajaxWait: function(waitStatus, waitNode, waitError){
			var vfo = jQuery.classBehaviours.handlers.validateForm;
			// update the status
//			waitNode.innerHTML = (waitStatus>-1) ? 'Loading: ' + Math.round(waitStatus * 100) + '%' : 'Error: ' + waitError ;
			// show the element (it may have been hidden by default)
//			waitNode.style.display = 'block';
		},
		ajaxReplace: function(replaceXml, replaceNode, replaceText){
			var vfo = jQuery.classBehaviours.handlers.validateForm;
			// update the content
			replaceNode.innerHTML = replaceText.split('<root>')[1].split('</root>')[0];
			// restore the focus
			focussedNode = document.getElementById(vfo.focused);
			if(focussedNode!=null) focussedNode.focus();
			// re-apply the classbehaviours
			jQuery.classBehaviours.parser.parseNode(replaceNode.parentNode.parentNode);
		},
		// events
		submitted: function(that, override){			
			var node = (typeof(this.nodeName)=='undefined') ? that : this ;
			var vfo = jQuery.classBehaviours.handlers.validateForm;
			var queryString = '';
			// get all elements of this form
			formNode = jQuery.classBehaviours.utilities.rootNode(node, 'FORM');
			rootNode = jQuery.classBehaviours.utilities.rootNode(node, 'FIELDSET');
			allNodes = rootNode.getElementsByTagName('*');
			// override the checks if asked
			if(override==true){
				allNodes = new Array();
				testValidated = true;
			}
			// for all elements
			for(var a=0; a<allNodes.length; a++){
				// assume the test failed
				testValidated = false;
				// if the element is marked with the test_* class, trigger its onchange event
				if(allNodes[a].nodeName=='INPUT' || allNodes[a].nodeName=='SELECT' || allNodes[a].nodeName=='TEXTAREA')
					if(allNodes[a].className.indexOf('test_')>-1)
						if(typeof(allNodes[a].onchange)!='undefined')
							testValidated = allNodes[a].onchange();
			}
			// update the summary
			summaryResult = vfo.summary(rootNode);
			// check how the form is to be submitted
			replySubmit = jQuery.classBehaviours.utilities.getClassParameter(rootNode, 'submit', null);
			if(replySubmit=='ajax'){
				// submit the form using ajax
				referingId = jQuery.classBehaviours.utilities.getClassParameter(rootNode, 'target', null);
				referingId = jQuery.classBehaviours.utilities.getClassParameter(node, 'target', referingId);
				referingObject = (referingId==null) ? node : document.getElementById(referingId) ;
				post = 'ajax=' + node.getAttribute('id') + '&section=' + jQuery.classBehaviours.utilities.getClassParameter(node, 'section', '');
				if(summaryResult) jQuery.classBehaviours.ajax.addSubmit(node, vfo.ajaxReplace, vfo.ajaxWait, post, referingObject);
				return false;
			}else{
				// allow the form to submit normally
				return summaryResult;
			}
/* TODO:
	- submit via iframe
*/
		}
	}
		jQuery.classBehaviours.handlers.validateForm.tests = {
			resubmit: {
				name: 'resubmit',
				apply: function(node){
					node.onchange = this.validate;
					node.onfocus = this.store;
					return true;
				},
				store: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					vfo.focused = node.id;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.submitted(node, true);
				}
			},
			email: {
				name: 'email',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/^[\w\.\-\,\+]+@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/)!=null);
				}
			},
			nlPhone: {
				name: 'nlPhone',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/(^\+[0-9]{2}|^\+[0-9]{2}\(0\)|^\(\+[0-9]{2}\)\(0\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\-\s]{10}$)/)!=null);
				}
			},
			nlPostal: {
				name: 'nlPostal',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/^[0-9]{4}\s{0,1}[a-zA-Z]{2}$/)!=null);
				}
			},
			isoDate: {
				name: 'isoDate',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/^\d{4}\-\d{1,2}\-\d{1,2}$/)!=null);
				}
			},
			numeric: {
				name: 'numeric',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/^[0-9]+$/)!=null);
				}
			},
			currency: {
				name: 'currency',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/^[0-9]+(\.[0-9]{1,2})?$/)!=null);
				}
			},
			alphaNumeric: {
				name: 'alphaNumeric',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value.match(/^[a-zA-Z0-9]/)!=null);
				}
			},
			nlBank: {
				name: 'nlBank',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					inputValue = node.value;
					if (inputValue.length!=9){
						return vfo.update(node, false);
					}else{
						numericTotal = 0;
						for (a=0; a<inputValue.length; a++) numericTotal += inputValue.substr(a, 1) * (9 - a);
						return vfo.update(node, numericTotal%11==0);
					}
				}
			},
			anyRadio: {
				name: 'anyRadio',
				apply: function(node){
					// if this node contains inputs
					allInputs = node.getElementsByTagName('INPUT');
					if(allInputs.length==0) allInputs = new Array(node);
					// apply the event handler and classname to all child nodes
					for(var a=0; a<allInputs.length; a++){
						if(allInputs[a].type=='radio'){
							allInputs[a].className = node.className;
							allInputs[a].onclick = (navigator.userAgent.indexOf('MSIE ')>-1) ? this.delay : this.validate ;
							allInputs[a].onchange = (navigator.userAgent.indexOf('MSIE ')>-1) ? this.delay : this.validate ;
						}
					}
					// report success
					return true;
				},
				delay: function(id){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					// trigger the validation
					setTimeout('jQuery.classBehaviours.handlers.validateForm.tests.anyRadio.validate(document.getElementById("'+node.id+'"))', 200);
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					// default validation
					anyChecked = false;
					// get all inputs with this name
					allInputs = document.getElementsByTagName('input');
					// for all inputs, if the input has the same name, if the input is checked set the validator to true
					for(var a=0; a<allInputs.length; a++) if(allInputs[a].name == node.name && allInputs[a].type == 'radio') if(allInputs[a].checked) anyChecked = true;
					// return the result
					return vfo.update(node, anyChecked);
				}
			},
			anyCheckbox: {
				name: 'anyCheckbox',
				apply: function(node){
					// if this node contains inputs
					allInputs = node.getElementsByTagName('INPUT');
					if(allInputs.length==0) allInputs = new Array(node);
					// apply the event handler and classname to all child nodes
					for(var a=0; a<allInputs.length; a++){
						if(allInputs[a].type=='checkbox'){
							allInputs[a].className = node.className;
							allInputs[a].onclick = (navigator.userAgent.indexOf('MSIE ')>-1) ? this.delay : this.validate ;
							allInputs[a].onchange = (navigator.userAgent.indexOf('MSIE ')>-1) ? this.delay : this.validate ;
						}
					}
					// report success
					return true;
				},
				delay: function(id){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					// trigger the validation
					setTimeout('jQuery.classBehaviours.handlers.validateForm.tests.anyCheckbox.validate(document.getElementById("'+node.id+'"))', 200);
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					// default validation
					anyChecked = false;
					// get all inputs from the parentnode
					parentNode = jQuery.classBehaviours.utilities.rootNode(node, 'TR');
					allInputs = parentNode.getElementsByTagName('input');
					// for all inputs, if this checkbox is checked remember is
					for(var a=0; a<allInputs.length; a++) if(allInputs[a].checked) anyChecked = true;
					// return the result
					return vfo.update(node, anyChecked);
				}
			},
			notEmpty: {
				name: 'notEmpty',
				apply: function(node){
					node.onchange = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					return vfo.update(node, node.value!="");
				}
			},
			password: {
				name: 'password',
				apply: function(node){
					node.onchange = this.validate;
					node.onblur = this.validate;
					return true;
				},
				validate: function(that){
					var node = (typeof(this.nodeName)=='undefined') ? that : this ;
					var vfo = jQuery.classBehaviours.handlers.validateForm;
					// default validation
					wasRepeated = false;
					// get the target field to compare
					repeatedId = jQuery.classBehaviours.utilities.getClassParameter(node, "clone", null);
					// if the id is valid
					if(repeatedId!=null){
						repeatedValue = document.getElementById(repeatedId).value;
						wasRepeated = (repeatedValue==node.value && repeatedValue!='' && node.value!='');
					}
					// report back
					return vfo.update(node, wasRepeated);
				}
			}
		}
		jQuery.classBehaviours.handlers.validateForm.custom = {
			apply: function(node){
				node.onchange = this.validate;
				return true;
			},
			validate: function(that){
				var node = (typeof(this.nodeName)=='undefined') ? that : this ;
				var vfo = jQuery.classBehaviours.handlers.validateForm;
				customRegId = jQuery.classBehaviours.utilities.getClassParameter(node, 'test', null);
				customRegString = document.getElementById(customRegId).value;
				customRegExp = new RegExp(customRegString);
				return vfo.update(node, node.value.match(customRegExp)!=null);
			}
		}	

	// add this addon to the jQuery object
	if(typeof(jQuery.fn)!='undefined'){
		// extend jQuery with this method
		jQuery.fn.validateForm = function(){
			return this.each(
				function(){
					jQuery.classBehaviours.handlers.validateForm.start(this);
				}
			);
		};
		// set the event handler for this jQuery method
		$(document).ready(
			function(){
				$(".validateForm").validateForm();
			}
		);
	}


