/////////////////////
// Validation API. //
/////////////////////
function COMPOSE_SecureForm(SecureForm) {  // Form validation API initialization.
	// Setup events.
	COMPOSE.AddEvent(SecureForm, 'submit', this.Validate);

	this.form = SecureForm;
	this.form._parentAPI = this;
	this.AuthenticationRequired = true;
	this.form.AuthenticationFields = new Array();
	
	// Find out which fields need authentication.
	for (var i=0;i<this.form.length;i++) {
		var FormField = this.form[i];
		
		// Setup the auth status and methods.
		if (FormField.getAttribute('auth-required')) {
			FormField.AuthenticationRequired = true;
		};
		
		if (FormField.getAttribute('auth-error')) {
			FormField.AuthenticationError = FormField.getAttribute('auth-error');
		};
		
		if (FormField.getAttribute('auth-methods')) {
			FormField.RawAuthenticationMethods = FormField.getAttribute('auth-methods').substring(1, FormField.getAttribute('auth-methods').length).toUpperCase().split('$');
			FormField.AuthenticationMethods = new Array;
		};
		
		// In case the user sets required, but has not set any methods.
		if (FormField.AuthenticationRequired && !FormField.AuthenticationMethods) {
			alert("The field ["+FormField.name+"] is flagged as requiring authentication but does not specify any methods.");
			FormField.AuthenticationRequired = false;
		};
		
		// Add the field to the authentication fields
		if (FormField.AuthenticationMethods) {		
			this.form.AuthenticationFields[this.form.AuthenticationFields.length] = FormField;
		};
		
		// If the field has an error, display the message
		if (FormField.AuthenticationError) {
			this.ThrowError(FormField, FormField.getAttribute('auth-error'));
		};
	};

	// Evaluate the methods to be used to authenticate the fields.
	for (var i=0;i<this.form.AuthenticationFields.length;i++) {
		var AuthenticationField = this.form.AuthenticationFields[i];
		
		// Evaluate each method.
		for (var ii=0;ii<AuthenticationField.RawAuthenticationMethods.length;ii++) {
			var Method = new Object;
			Method.RawAuthentication = AuthenticationField.RawAuthenticationMethods[ii].toUpperCase();
			
			if (Method.RawAuthentication.indexOf('.')>0) {
			
				// For methods that have sub properties, this may be expanded to a function to grab items sub
				// properties, as such you could have unlimited amount of sub properties, eg obj.prop.prop.prop.
				
			} else { // Method only for now
				if (Method.RawAuthentication.indexOf('(')>0) { // If the method is giving arguments.
					Method.name = Method.RawAuthentication.substring(0, Method.RawAuthentication.indexOf('(')).toUpperCase();
					Method.properties = Method.RawAuthentication.substring(Method.RawAuthentication.indexOf('(')+1, Method.RawAuthentication.length-1).split(',');
				} else {
					Method.name = Method.RawAuthentication.toUpperCase();
				}
			}

			// Add the authentication method to the fields AuthenticationMethods.
			AuthenticationField.AuthenticationMethods[AuthenticationField.AuthenticationMethods.length] = Method;
		}
	}	
}

COMPOSE_SecureForm.prototype.Validate = function(e) { // Validate.
	var AlphanumLength = "ALPHANUMERICLENGTH";
	var NonAlphanumLength = "NONALPHANUMERICLENGTH";
	var NoAlphanum = "NOALPHANUMERIC";
	var NoNonAlphanum = "NONONALPHANUMERIC";
	var Email = "EMAIL";
	var Postcode = "POSTCODE";
	var PhoneType = "PHONETYPE";
	var Date = "DATE";
	var Time = "TIME";
	var StrictLength = "STRICTLENGTH";
	
	var Select = "SELECT";
	
	for (var i=0;i<this.AuthenticationFields.length;i++) { // Remove old notices.
		var AuthenticationField = this.AuthenticationFields[i];
		
		if (AuthenticationField.ErrMessage) {
			AuthenticationField.ErrMessage.parentNode.removeChild(AuthenticationField.ErrMessage);
			delete AuthenticationField.ErrMessage;
		}
	}

	for (var i=0;i<this.AuthenticationFields.length;i++) {
		// Find the current field.
		var AuthenticationField = this.AuthenticationFields[i];
		
		// Pass the submit event object over.
		AuthenticationField.SubmitEvent = e;
		
		// Trim all values to remove spaces and return chars at the end.
		AuthenticationField.value = trim(AuthenticationField.value);
		
		if ((AuthenticationField.AuthenticationRequired) || (!AuthenticationField.AuthenticationRequired && AuthenticationField.AuthenticationMethods && AuthenticationField.value.length > 0)) {
		
			for (var ii=0;ii<AuthenticationField.AuthenticationMethods.length;ii++) {
				var Authenticate = AuthenticationField.AuthenticationMethods[ii];
				
				if (Authenticate.name == StrictLength) {
				
					if (!AuthenticationField.value.length.between(Authenticate.properties[0], Authenticate.properties[1])) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field length is not valid, please enter a value between <strong>' + Authenticate.properties[0] + '</strong> and <strong>' + Authenticate.properties[1] + '</strong> characters in length.');
					}
								
				} if (Authenticate.name == AlphanumLength) {
				
					if (!AuthenticationField.value.replace(/\W/g, "").length.between(Authenticate.properties[0], Authenticate.properties[1])) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field length is not valid, please enter a value between <strong>' + Authenticate.properties[0] + '</strong> and <strong>' + Authenticate.properties[1] + '</strong> characters in length.');
					}
								
				} if (Authenticate.name == NonAlphanumLength) {

					if (!AuthenticationField.value.replace(/\w/g, "").length.between(Authenticate.properties[0], Authenticate.properties[1])) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field length is not valid because it contains too many or too little non-alphanumeric characters such as \\/:*?<>|. Please enter a value between <strong>' + Authenticate.properties[0] + '</strong> and <strong>' + Authenticate.properties[1] + '</strong>.');
					}
				
				} if (Authenticate.name == NoAlphanum) {
				
					if (AuthenticationField.value.replace(/\W/g, "").length > 0) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field cannot contain any alphanumeric characters, characters such as \\/:*?<>| are valid.');
					}
				
				} if (Authenticate.name == NoNonAlphanum) {
				
					if (AuthenticationField.value.replace(/\w/g, "").length > 0) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field cannot contain any non-alphanumeric characters, characters such as \\/:*?<>| are not valid.');
					}
				
				} if (Authenticate.name == Email) {

					if (!AuthenticationField.value.match(/^[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$/))
					{
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field is not a valid email, please enter an email address in <strong>x@x.xx</strong> format.');
					}
					
				} if (Authenticate.name == Date) {
				
					if (!AuthenticationField.value.match( /^(3[01]|0[1-9]|[12]\d)\/(0[1-9]|1[012])\/\d{4}/))
					{
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field is not a valid date, dates should be entered in <strong>dd/mm/yyyy</strong> format.');
					}
					
				} if (Authenticate.name == Time) {
					if (!AuthenticationField.value.match(/([0-1][0-9]|[2][0-4])(:[0-5][0-9])/))
					{
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field is not a valid time, times should be entered in <strong>hh:mm</strong> format.');
					}
					
				} if (Authenticate.name == Postcode) {
				
					if (!new RegExp(/[A-PR-UWYZ][A-HK-Y0-9][A-HJKSTUW0-9]?[ABEHMNPRVWXY0-9]? {0,1}[0-9][ABD-HJLN-UW-Z]{2}/).test(AuthenticationField.value.toUpperCase())) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field is not a valid UK Postcode.</ul>');
					}
										
				} if (Authenticate.name == PhoneType) {
					if (AuthenticationField.value.replace(/[0-9\-\+\(\) ]/g, "").length > 0)  {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field is not a valid telephone type, it contains illegal characters.');
					}
					if (AuthenticationField.value.length == 0) {
						// move the page to the first element that makes an error..								
						// build the visual cue.
						return this._parentAPI.ThrowError(AuthenticationField, 'The field is not a valid telephone type.');
					}
					
				} if (Authenticate.name == Select) {
					
				}
			}
		}
	}
}

COMPOSE_SecureForm.prototype.CancelSubmit = function(e) {
	if(COMPOSE.Browser.isIE){e.cancelBubble=true;e.returnValue=false;}
	else{e.preventDefault();e.stopPropagation();}return false;
}

COMPOSE_SecureForm.prototype.ThrowError = function(AuthenticationField, err) {
	// create the error message
	var ErrMessage = document.createElement('p');
	ErrMessage.AuthenticationField = AuthenticationField;
	ErrMessage.className = 'notice';
	ErrMessage.style.position = 'absolute';
	ErrMessage.style.zIndex = 1000;
	ErrMessage.style.left = (_getX(AuthenticationField)+15) + 'px';
	ErrMessage.style.top = (_getY(AuthenticationField)+AuthenticationField.offsetHeight) + 'px';
	
	ErrMessage.onmouseover = function() {
		this.style.cursor = 'pointer';
	}
	
	ErrMessage.onclick = function() {
		this.AuthenticationField.KillError();
	}
		
	// write the error message in.
	ErrMessage.innerHTML = '<img src="/images/pointer2.gif" style="position: absolute;margin-top: -13px;"><strong>Invalid Field.</strong><br />';
	ErrMessage.innerHTML += err;
	
	// render the object.
	AuthenticationField.parentNode.appendChild(ErrMessage);
	AuthenticationField.ErrMessage = ErrMessage;
	AuthenticationField.focus();
	
	AuthenticationField.KillError = function() {
		this.parentNode.removeChild(this.ErrMessage);
		delete this.ErrMessage;
		delete this.KillError;
		delete this.onkeypress;
	}
	
	AuthenticationField.onkeypress = function() {
		this.KillError();
		delete this.onkeypress;
	}
	
	// cancel the submit event if the field is carrying it.
	if (AuthenticationField.SubmitEvent) {
		return AuthenticationField.form._parentAPI.CancelSubmit(AuthenticationField.SubmitEvent);
	}
}

function makeNotice(obj, verror) {	
	// create the error message
	var sp = document.createElement('p');
	
	sp.targetElement = obj;
	
	// set its CSS class and style positioning.
	sp.className = 'notice';
	sp.style.position = 'absolute';
	sp.style.left = (_getX(obj)+15) + 'px';
	sp.style.top = (_getY(obj)+obj.offsetHeight) + 'px';
	sp.style.zIndex = 1000;
	
	sp.onmouseover = function() {
		this.style.cursor = 'pointer';
	}
	
	sp.onclick = function() {
		this.parentNode.removeChild(this);
	}
		
	// write the error message in.
	if (!obj.Ammend) {
		sp.innerHTML = '<img src="/images/pointer2.gif" style="position: absolute;margin-top: -13px;"><strong>Invalid Field</strong><br />';
	} else {
		obj.Ammend = false;
		sp.innerHTML = '<strong>Also</strong><br />';
		sp.style.marginTop = '-1';
		sp.style.borderTop = '0';
	}
	
	sp.innerHTML += verror + '<br style=\"clear: both;\" />';
	
	// render the object.
	obj.parentNode.appendChild(sp);
}

function COMPOSE_ThrowErrorBox(obj, message) {
	if (obj.parentForm) //n.getForm
	{
		if (obj.parentForm.didValidate){ // what is the status of the forms validation?
			obj.parentForm.didValidate = false;
			obj.focus();
		}
	}

	// if DHTML is supported then make the gui message, else display an alert box.
	if (COMPOSE.Browser.isDHTML) { makeNotice(obj, message) } else { alert(message) };
}