/**
 * Ext.ux.plugins
 *
 * @author    Ing. Jozef Sakálo <jsakalos@aariadne.com>
 * @copyright (c) 2007, by Ing. Jozef Sakálo
 * @date      24. November 2007
 * @version   $Id: Ext.ux.plugins.js 596 2007-11-25 13:40:43Z jozo $
 */
 
Ext.namespace('Ext.ux', 'Ext.ux.plugins');
 
/**
 * Remote Validator
 * Makes remote (server) field validation easier
 *
 * To be used by form fields like TextField, NumberField, TextArea, ...
 */
Ext.ux.plugins.RemoteValidator = {
	init:function(field) {
		 // save original functions
		 var isValid = field.isValid;
		 var validate = field.validate;
		 var markInvalid = field.markInvalid;
		 var lastValue = null;
 
		 // apply remote validation to field
		 Ext.apply(field, {
				remoteValid:false,
				requestExecuting: true,
 				isValid:function(preventMark) {
					return isValid.call(this, preventMark) && !this.requestExecuting && this.remoteValid;
				},
 				validate:function() {
					if(!validate.call(this)) {
						if (this.remoteValidationTask)
							this.remoteValidationTask.cancel();
						this.hideLoadingMask();
						return false;
					}

					if (this.requestExecuting) {
						 return false;
					}
					else {
						if(this.remoteValid) {
							 this.markValid();
							 return true;
						}
						else {
							 this.markInvalid(this.reason);
							 return false;
						}
					}

					return false;
				},

				getVlmLeftPos: function() {
					if (Ext.isIE)
						return this.width;
					else
						return this.getEl().getOffsetsTo(this.getEl().parent())[0] + this.getEl().getWidth() + 2;
				},
 
				markValid: function() {
					this.clearInvalid();
					
					if (!this.validMarked){
						this.validMarked = Ext.DomHelper.append(this.getEl().parent(), {
							cls:'x-form-valid-icon',
							style: 'left:' + this.getVlmLeftPos() + 'px'
						}, true)
					}
				},
 
 				unMarkValid: function() {
					if (this.validMarked) {
						this.validMarked.remove();
						delete this.validMarked;
					}
 				},
 
 				markInvalid: function(reason) {
 					this.unMarkValid();
 					markInvalid.call(this, reason);
 				},
 
 				showLoadingMask: function() {
					this.requestExecuting = true;
					this.clearInvalid();
					this.unMarkValid();	 						

					if (!this.validLoadMask){
						this.validLoadMask = Ext.DomHelper.append(this.getEl().parent(), {
							cls:'x-form-loading-icon',
							style: 'left:' + this.getVlmLeftPos() + 'px'
						}, true)
					}
 				},
 
 				hideLoadingMask: function() {
 					this.requestExecuting = false;
					if (this.validLoadMask) {
						this.validLoadMask.remove();
						delete this.validLoadMask;
					}
 				},
 
				// private - remote validation request
				validateRemote:function() {
					//Only send a vlaidation request if the value has changed
					if (this.getValue() != this.lastValue) {
						this.lastValue = this.getValue();
						
						this.rvOptions.params = this.rvOptions.params || {};
						this.rvOptions.params.field = this.name;
						this.rvOptions.params.value = this.getValue();
						this.showLoadingMask();
						this.remoteValid = false;
						Ext.Ajax.request(this.rvOptions);
					}
				},
 
				// private - remote validation request success handler
				rvSuccess:function(response, options) {
					this.hideLoadingMask();
					var o;
					try {
						 o = Ext.decode(response.responseText);
					}
					catch(e) {
						 throw this.cannotDecodeText;
						 return false
					}
					
					if('object' !== typeof o) {
						 throw this.notObjectText;
						 return false
					}
					if(true !== o.success) {
						 throw this.serverErrorText + ': ' + o.error;
						 return false
					}
					
					var names = this.rvOptions.paramNames;
					this.remoteValid = true === o[names.valid];
					this.reason = o[names.reason];
					this.validate();
				},
 
				// private - remote validation request failure handler
				rvFailure:function(response, options) {
					this.hideLoadingMask();
					throw this.requestFailText
					return false
				},
 
				// private - runs from keyup event handler
				filterRemoteValidation:function(e) {
					if(!e.isNavKeyPress()) {
						 this.requestExecuting = true;
						 this.remoteValidationTask.delay(this.remoteValidationDelay);
					}
				},
				
				// private - runs from field value changed event handler
				forceRemoteValidation:function(e) {
					this.remoteValidationTask.delay(0);
				}
		 });
 
		 // remote validation defaults
		 Ext.applyIf(field, {
				remoteValidationDelay:500,
				reason:'Server has not yet validated the value',
				cannotDecodeText:'Cannot decode json object',
				notObjectText:'Server response is not an object',
				serverErrorText:'Server error',
				requestFailText:'Server request failed'
		 });
 
		 // install event handlers on field render
		 field.on({
				render:{single:true, scope:field, fn:function() {
					this.remoteValidationTask = new Ext.util.DelayedTask(this.validateRemote, this);
					this.el.on({
						'keyup': this.filterRemoteValidation,
						'change': this.forceRemoteValidation,
						scope: this
					})
				}}
		 });
 
		 // setup remote validation request options
		 field.rvOptions = field.rvOptions || {};
		 Ext.applyIf(field.rvOptions, {
				method:'post',
				scope: field,
				success: field.rvSuccess,
				failure: field.rvFailure,
				paramNames: {
					valid:'valid'
					,reason:'reason'
				}
		});
	}
};