//validates a form field in real-time based on validations you assign to it
var LiveValidation = function(element, errorlocation){
	this.initialize(element);
}

/** element types constants****/
LiveValidation.TEXTAREA 	= 1;
LiveValidation.TEXT 		= 2;
LiveValidation.PASSWORD 	= 3;
LiveValidation.CHECKBOX 	= 4;
LiveValidation.SELECT 		= 5;
LiveValidation.FILE 		= 6;

//pass an array of LiveValidation objects and it will validate all of them
LiveValidation.massValidate = function(validations){
	var returnValue = true;
	for(var i = 0, len = validations.length; i < len; ++i ){
		var valid = validations[i].validate();
		if(returnValue) returnValue = valid;
	}
	return returnValue;
}

//prototype
LiveValidation.prototype = {
	//initialises all of the properties and events
	initialize: function(element){
		var self = this;
		this.element = element.nodeName ? element : document.getElementById(element);
		this.validations = [];
		this.elementType = this.getElementType();
		this.form = this.element.form;
		this.onValid = function(){ this.removeMessage(); this.removeFieldClass(); };
		this.onInvalid = function(){ this.insertMessage(); this.addFieldClass(); }; //function to execute when field fails validation 
		this.onlyOnBlur = true; //whether you want it to validate as you type or only on blur
		// add to form if it has been provided
		if(this.form){
			this.formObj = LiveValidationForm.getInstance(this.form);
			this.formObj.addField(this);
		}

		// events
		// collect old events
		this.oldOnFocus = this.element.onfocus || function(){};
		this.oldOnBlur = this.element.onblur || function(){};
		this.oldOnClick = this.element.onclick || function(){};
		this.oldOnChange = this.element.onchange || function(){};
		this.oldOnKeyup = this.element.onkeyup || function(){};
		this.element.onfocus = function(e){ self.doOnFocus(e); return self.oldOnFocus.call(this, e); }
		if(!this.onlyOnSubmit){
			switch(this.elementType){
				case LiveValidation.CHECKBOX:
				this.element.onclick = function(e){ self.validate(); return self.oldOnClick.call(this, e); }
				// let it run into the next to add a change event too
				case LiveValidation.SELECT:
				case LiveValidation.FILE:
				this.element.onchange = function(e){ self.validate(); return self.oldOnChange.call(this, e); }
				break;
				default:
				this.element.onblur = function(e){ self.doOnBlur(e); return self.oldOnBlur.call(this, e); }
			}
		}
	},

	//destroys the instance's events (restoring previous ones) and removes it from any LiveValidationForms
	destroy: function(){
		if(this.formObj){
			// remove the field from the LiveValidationForm
			this.formObj.removeField(this);
			// destroy the LiveValidationForm if no LiveValidation fields left in it
			this.formObj.destroy();
		}

		// remove events - set them back to the previous events
		this.element.onfocus = this.oldOnFocus;
		if(!this.onlyOnSubmit){
			switch(this.elementType){
				case LiveValidation.CHECKBOX:
				this.element.onclick = this.oldOnClick;
				// let it run into the next to add a change event too
				case LiveValidation.SELECT:
				case LiveValidation.FILE:
				this.element.onchange = this.oldOnChange;
				break;
				default:
				if(!this.onlyOnBlur) this.element.onkeyup = this.oldOnKeyup;
				this.element.onblur = this.oldOnBlur;
			}
		}
		this.validations = [];
		this.removeMessageAndFieldClass();
	},

	//adds a validation to perform to a LiveValidation object
	add: function(validationFunction, validationParamsObj){
		this.validations.push( {type: validationFunction, params: validationParamsObj || {} } );
		return this;
	},

	//removes a validation from a LiveValidation object - must have exactly the same arguments as used to add it 
	remove: function(validationFunction, validationParamsObj){
		var found = false;
		for( var i = 0, len = this.validations.length; i < len; i++ ){
			if( this.validations[i].type == validationFunction ){
				if (this.validations[i].params == validationParamsObj) {
					found = true;
					break;
				}
			}
		}
		if(found) this.validations.splice(i,1);
		return this;
	},

	// sets the focused flag to false when field loses focus 
	doOnBlur: function(e){
		this.focused = false;
		this.validate(e);
	},

	//sets the focused flag to true when field gains focus 
	doOnFocus: function(e){
		this.focused = true;
		this.removeMessageAndFieldClass();
	},

	//gets the type of element, to check whether it is compatible
	getElementType: function(){
		switch(true){
			case (this.element.nodeName.toUpperCase() == 'TEXTAREA'):
				return LiveValidation.TEXTAREA;
			case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'TEXT'):
				return LiveValidation.TEXT;
			case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'PASSWORD'):
				return LiveValidation.PASSWORD;
			case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'CHECKBOX'):
				return LiveValidation.CHECKBOX;
			case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'FILE'):
				return LiveValidation.FILE;
			case (this.element.nodeName.toUpperCase() == 'SELECT'):
				return LiveValidation.SELECT;
		}
	},

	//loops through all the validations added to the LiveValidation object and checks them one by one
	doValidations: function(){
		this.validationFailed = false;
		for(var i = 0, len = this.validations.length; i < len; ++i){
			var validation = this.validations[i];
			switch(validation.type){
				case Validate.Presence:
				case Validate.Confirmation:
				case Validate.Acceptance:
				this.displayMessageWhenEmpty = true;
				this.validationFailed = !this.validateElement(validation.type, validation.params); 
				break;
				default:
				this.validationFailed = !this.validateElement(validation.type, validation.params);
				break;
			}
			if(this.validationFailed) return false;
		}
		this.message = '';
		return true;
	},

	//performs validation on the element and handles any error (validation or otherwise) it throws up
	validateElement: function(validationFunction, validationParamsObj){
		var value = this.element.value;
		if(validationFunction == Validate.Acceptance){
			value = this.element.checked;
		}
		var isValid = true;
		try{	
			validationFunction(value, validationParamsObj);
		} catch(error) {
				if(error instanceof Validate.Error){
				if( value !== '' || (value === '' && this.displayMessageWhenEmpty) ){
					this.validationFailed = true;
					this.message = error.message;
					isValid = false;
				}
			}else{
					throw error;
			}
		}finally{
			return isValid;
		}
	},

	//makes it do the all the validations and fires off the onValid or onInvalid callbacks
	validate: function(){
		if(!this.element.disabled){
			var isValid = this.doValidations();
			if(isValid){
				if(this.element.type.toUpperCase() == 'CHECKBOX'){this.onValid();}
				return true;
			}else {
				this.onInvalid();
				return false;
			}
		}else{
			return true;
		}
	},

	//enables the field
	enable: function(){
		this.element.disabled = false;
	return this;
	},

	//disables the field and removes any message and styles associated with the field
	disable: function(){
		this.element.disabled = true;
	this.removeMessageAndFieldClass();
	return this;
	},

	//inserts the element containing the message in place of the element that already exists (if it does)
	insertMessage: function(){
		this.removeMessage();
		elementToInsert = this.message;
		this.element.nextSibling.nextSibling.className = 'invalid';
		this.element.nextSibling.nextSibling.innerHTML=elementToInsert;
	},

	//changes the class of the field based on whether it is valid or not
	addFieldClass: function(){
		this.removeFieldClass();
		if(!this.validationFailed){
			this.element.className = 'invalid_field';
		}else{
			this.element.className = 'invalid_field';
		}
	},

	removeMessage: function(){
		this.element.nextSibling.nextSibling.innerHTML='';
		this.element.nextSibling.nextSibling.className ='';
	},

	removeFieldClass: function(){
		this.element.className = '';
	},

	removeMessageAndFieldClass: function(){
		this.removeMessage();
		this.removeFieldClass();
	}
}

//handles validation of LiveValidation fields belonging to this form on its submittal
var LiveValidationForm = function(element){
	this.initialize(element);
}

//namespace to hold instances
LiveValidationForm.instances = {};

//gets the instance of the LiveValidationForm if it has already been made or creates it if it doesnt exist
LiveValidationForm.getInstance = function(element){
	var rand = Math.random()* Math.random();
	if(!element.id) element.id = 'formId_' + rand.toString().replace(/\./, '') + new Date().valueOf();
	if(!LiveValidationForm.instances[element.id]) LiveValidationForm.instances[element.id] = new LiveValidationForm(element);
	return LiveValidationForm.instances[element.id];
}

LiveValidationForm.prototype = {
	//constructor for LiveValidationForm - handles validation of LiveValidation fields belonging to this form on its submittal
	initialize: function(element){
		this.name = element.id;
		this.element = element;
		this.fields = [];
		// preserve the old onsubmit event
		this.oldOnSubmit = this.element.onsubmit || function(){};
		var self = this;
		this.element.onsubmit = function(e){
			return (LiveValidation.massValidate(self.fields)) ? self.oldOnSubmit.call(this, e || window.event) !== false : false;
		}
	},
	//adds a LiveValidation field to the forms fields array
	addField: function(newField){
		this.fields.push(newField);
	},
	//removes a LiveValidation field from the forms fields array
	removeField: function(victim){
		var victimless = [];
		for( var i = 0, len = this.fields.length; i < len; i++){
			if(this.fields[i] !== victim) victimless.push(this.fields[i]);
		}
		this.fields = victimless;
	},
	//destroy this instance and its events
	destroy: function(force){
		// only destroy if has no fields and not being forced
		if (this.fields.length != 0 && !force) return false;
		// remove events - set back to previous events
		this.element.onsubmit = this.oldOnSubmit;
		// remove from the instances namespace
		LiveValidationForm.instances[this.name] = null;
		return true;
	}

}

//This class contains all the methods needed for doing the actual validation itself
var Validate = {
	Presence: function(value, paramsObj){
		if(value === '' || value === null || value === undefined){ 
			Validate.fail('Gelieve dit vak in te vullen');
		}
		return true;
	},

	//validates that the value is numeric, fails when value is not an integer
	Numericality: function(value, paramsObj){
		var minimum = paramsObj.minimum || 0;
		if (!isFinite(value)) Validate.fail('?');
		if( value < Number(minimum) ) Validate.fail('?');
		return true;
	},

	//validates against a RegExp pattern
	Format: function(value, paramsObj){
		var value = String(value);
		var paramsObj = paramsObj || {};
		var message = paramsObj.failureMessage || "Not valid!";
		var pattern = paramsObj.pattern || /./;
		var negate = paramsObj.negate || false;
		if(!negate && !pattern.test(value)) Validate.fail(message); // normal
		if(negate && pattern.test(value)) Validate.fail(message); // negated
		return true;
	},

	//validates that the field contains a valid email address
	Email: function(value, paramsObj){
		var message = "Gelieve een geldig email adres op te geven";
		Validate.Format(value, { failureMessage: message, pattern: /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i } );
		return true;
	},

	//validates that the value is true (for use primarily in detemining if a checkbox has been checked)
	Acceptance: function(value, paramsObj){
		if(!value){ 
			Validate.fail('U dient hiermee akkoord te gaan om tickets te bestellen');
		}
		return true;
	},

	// shortcut for failing throwing a validation error
	fail: function(errorMessage){
		throw new Validate.Error(errorMessage);
	},

	Error: function(errorMessage){
		this.message = errorMessage;
		this.name = 'ValidationError';
	}
}

//define which functions to validate
var checknaam = new LiveValidation('naam');
checknaam.add( Validate.Presence );

var checkemail = new LiveValidation('email');
checkemail.add( Validate.Presence );
checkemail.add( Validate.Email );

var checkok = new LiveValidation('ok');
checkok.add( Validate.Acceptance );

var checkleden = new LiveValidation('leden');
checkleden.add( Validate.Numericality );

var checknietleden = new LiveValidation('nietleden');
checknietleden.add( Validate.Numericality );

var checkvip = new LiveValidation('vip');
checkvip.add( Validate.Numericality );

//addition thingy
function update(rij){
	//fetch input en bereken
	leden		= document.getElementById('leden').value;
	nietleden	= document.getElementById("nietleden").value;
	vip			= document.getElementById("vip").value;
	prijs1=prijs2=prijs3=totaal=0
	if ( ! isNaN(leden)		)	{ prijs1 =     leden * 8  };
	if ( ! isNaN(nietleden))	{ prijs2 = nietleden * 10 };
	if ( ! isNaN(vip)		)	{ prijs3 =       vip * 20 };
	totaal=prijs1+prijs2+prijs3;

	if ( isNaN(leden)		)	{ leden		= 0 };
	if ( isNaN(nietleden)	)	{ nietleden = 0 };
	if ( isNaN(vip)			)	{ vip	 	= 0 };

	////Update rijen
	//define id's
	if ( rij == 1 ) { error = 'ledenerror' 		};
	if ( rij == 2 ) { error = 'nietledenerror'	};
	if ( rij == 3 ) { error = 'viperror' 		};
	//update de bewerkte rij, behalve als de form wordt gesubmit
	if ( rij !== 99 ) {
		document.getElementById(error).innerHTML=window['prijs'+rij]+'€';
	}

	////update totaal
	//reset de waarden
	document.getElementById('totaal').className = '';
	document.getElementById('totaal').innerHTML = '';
	//toon totaal al alle rijen ok zijn
	if ( leden >= 0 && nietleden >= 0 && vip >= 0 ) {
		document.getElementById('totaal').innerHTML=totaal+'€' ;
	}
	//toon error als er 0 tickets 
	if ( totaal == 0 ) {
		document.getElementById('totaal').innerHTML='Geen ticketten geselecteerd';
		document.getElementById('totaal').className = 'LV_invalid';
	}
}


