/**
 * $Id::                                                                       $
 * 
 * This file contains all classes that we need to manipulate forms on the fly.
 * 
 * @copyright 2008 Phase 4
 * @version 1.0
 * @link $URL::                                                                $
 * @author $Author::                                                           $
 * @package Phase4
 * @subpackage Forms
 */

/*
 * Register classes into subpackage Forms
 */
Phase4.registerClasses('Forms', ['FieldObserver']);

/**
 * Observes a field as defined by specified options. Takes care of
 * setting a fields default-value if needed, installs eventlisteners
 * and just makes the life of frontend programmers as easy as can be.
 * 
 * Note: As of now, defaulting is only supported for text-fields!
 * 
 * Known options are as follows:
 * 
 * Name					Type		Description
 * clearDefaultOnFocus	bool		If set to true, field will be cleared onFocus if it contains default value 
 * default				String		The default value that will be set if field is empty.
 * validate				Function	A callback function that will validate the field. If omitted, defaults to Form#present()
 */
Phase4.Forms.FieldObserver.prototype = {
	/**
	 * Constructor
	 * 
	 * @param {String,Object} field The field that shall be observed
	 * @param {Object} options The options that shall be applied to field
	 * @return {FieldObserver} A new instance of FieldObserver
	 */
	initialize: function(field, options) {
		//init & default datamembers
		this.field = $(field);
		this.options = options || {};
		
		//if we couldn't find field, we throw an exception
		if (!this.field) {
			throw('Requested field "' + field + '" does not exist');
		}
		
		//set bindings
		this.bindings = {};
		this.bindings.onBlur = this._onBlur.bindAsEventListener(this);
		this.bindings.onFocus = this._onFocus.bindAsEventListener(this);
		
		//fill field initially
		this.setDefaultValue();
		
		//setup observers
		this._setupObservers();
	},
	
	/**
	 * Destructor
	 */
	dispose: function() {
		//free up system resources
		for (var binding in this.bindings) {
			Event.stopObserving(this.bindings[binding]);
		}
	},
	
	/*
	 * Private methods
	 */
	
	/**
	 * Returns whether field needs to be cleared when it receives the focus.
	 * 
	 * @return {bool} True if clearing is needed, false otherwise
	 * @private
	 */
	_needsClearing: function() {
		var fieldValue = this.field.getValue();
		return (
			Object.isString(fieldValue) &&							//only strings are supported 
			this.options.clearDefaultOnFocus &&						//is crearing requested?
			this.options['default'] && 								//do we have a default value?
			fieldValue.strip()==this.options['default'].strip()		//is the fields value equal to its default value, ignoring leading/trailing whitespaces? 
		);
	},
	
	/**
	 * Returns whether we can and must default the field that we observe.
	 * 
	 * @return {bool} True if field can be and needs to be defaulted, false otherwise
	 * @private
	 */
	_needsDefaulting: function() {
		var fieldValue = this.field.getValue();
		return (
			Object.isString(fieldValue) &&							//only strings are supported
			fieldValue.blank() &&									//is the field currently blank?
			this.options['default']									//do we have4 a default value?
		);
	},
	
	/**
	 * Sets up the eventobservers needed for this object to work.
	 * 
	 * @private
	 */
	_setupObservers: function() {
		Event.observe(this.field, 'blur', this.bindings.onBlur);
		Event.observe(this.field, 'focus', this.bindings.onFocus);
	},
	
	/*
	 * Private callbacks
	 */
	
	/**
	 * Called when observed field is blurred.
	 * Validates field and calls setDefaultValue() is validation fails.
	 * 
	 * @param {Event} evt The event that is passed by eventlistener
	 * @private
	 */
	_onBlur: function(evt) {
		evt.stop();
		if (!this.validate()) this.setDefaultValue();
	},
	
	/**
	 * Called when observed field receives focus.
	 * Clears field of default if clearDefaultOnFocus is set.
	 * Otherwise calls setDefaultValue() and selects content.
	 * 
	 * @param {Event} evt The event that is passed by eventlistener
	 * @private
	 */
	_onFocus: function(evt) {
		evt.stop();
		
		if (this._needsClearing()) {
			this.field.clear();
		} else {
			this.setDefaultValue();
			this.field.select();
		}
	},
	
	/*
	 * Public methods
	 */
	
	/**
	 * Sets fields default value if specified and field is empty.
	 */
	setDefaultValue: function() {
		if (this._needsDefaulting()) {
			this.field.value = this.options['default'];
		}
	},
	
	/**
	 * If a validation function was specified will validate
	 * field by calling it. Otherwise validates field by 
	 * calling Form#present()
	 * 
	 * @return {bool} Trueif field is considered valid, false otherwise
	 */
	validate: function() {
		if (this.options.validate) {
			return this.options.validate(this.field);
		} else {
			return this.field.present();
		}
	}
};
