/**
 * AJAX form creation
 * @class cowboy.FormAjax
 * @implements {cowboy.Options}
 */
cowboy.FormAjax = new Class ({
	Implements: cowboy.Options,

	options: {
		invalidFieldMessage: 'Please fill this field',
		redirectDelay: 1000,
		onRequest: function() { return; },
		onSuccess: function(response) { return; },
		onFailure: function() { return; },
		redirect: null,
		dataContainer: null,
		beforeRequest: function() { return; }
	},

	/**
	 * Create a FormAjax
	 * @constructor
	 * @param  {Element}	form		Form element
	 * @param  {Object}		options		Form options
	 */
	initialize: function(form, options) {
		var _this = this;
		if (form instanceof HTMLElement) {
			this.form = form;

			this.options = Object.merge(this.options, options);

			// Set Options and overload options by properties in the form
			this.setElementOptions(this.options, form);

			this.form.setProperty('autocomplete', 'off');
			this.form.addEvent('submit', this.submit.bind(this));

			this.form.getElements('.required').each(function(el){
				el.addEvent('focus', _this.focus);
				el.addEvent('blur', _this.blur.bind(_this));
			});

			this.uploadReq = new cowboy.Request({
				onRequest: function() {
					return _this.options.onRequest();
				},

				onSuccess: function(response) {
					return _this.options.onSuccess(JSON.decode(response));
				},

				onFailure: function() {
					return _this.options.onFailure();
				}
			});
		}
		else if (form instanceof Object) {
			for (var i = form.length - 1; i >= 0; i--) {
				new cowboy.FormAjax(form[i], options);
			}
		}
	},

	/**
	 * Submit the form
	 * @method submit
	 * @param  {Event}	e	Event that triggers the submit
	 */
	submit: function(e) {
		e.stop();

		this.showErrors();

		if (this.form.getElements('.error').length === 0) {
			this.getFormData();

			this.options.beforeRequest();

			this.uploadReq.send({ url: this.form.getProperty('action') });
		}

		this.uploadReq.reset();
	},

	/**
	 * Event triggered on input focus
	 * @method focus
	 * @param  {Event}	e	Event when an input is focused
	 */
	focus: function(e){
		if(e.target.hasClass('error')) {
			this.removeClass('error');
			this.erase('placeholder');
		}
	},

	/**
	 * Event triggered on input blur
	 * @method blur
	 * @param  {Event}	e	Event when an input is blured
	 */
	blur: function(e){
		if (e.target.value === '') {
			e.target.setProperty('placeholder',this.options.invalidFieldMessage);
			e.target.addClass('error');
		}
	},

	/**
	 * Show the form errors
	 * @method showErrors
	 */
	showErrors: function() {
		_this = this;
		this.form.getElements('.required').each(function(el) {
			if (el.value === '') {
				el.addClass('error');
				el.setProperty('placeholder',_this.options.invalidFieldMessage);
			}
		});
	},

	/**
	 * Get data to submit
	 * @method getFormData
	 */
	getFormData: function() {
		var _this = this;

		var data = {};

		this.form.getElements('input[name]').each(function(el) {
			if (el.getProperty('type') == 'checkbox') {
				if (el.checked === true) {
					data[el.name] = el.value;
				}
				else {
					data[el.name] = 0;
				}
			}
			else if (el.getProperty('type') == 'radio') {
				if (el.checked === true) {
					data[el.name] = el.value;
				}
			}
			else if(el.getProperty('type') == 'file') {
				data[el.name] = el.files[0];
			}
			else {
				data[el.name] = el.value;
			}
		});

		this.form.getElements('select[name]').each(function(el){
			data[el.name] = el.value;
		});

		this.form.getElements('textarea[name]').each(function(el){
			data[el.name] = el.value;
		});

		if (this.options.dataContainer !== null) {
			Object.each(data, function(value, key) {
				_this.uploadReq.append(_this.options.dataContainer + '[' + key + ']', value);
			});
		}
		else {
			Object.each(data, function(value, key) {
				_this.uploadReq.append(key, value);
			});
		}
	}
});

@@ -560,16 +560,49 @@
 options%0A
+%09 * @param  %7BObject%7D%09%09callbacks%09%0A
 %09 */%0A%09in
@@ -633,16 +633,27 @@
  options
+, callbacks
 ) %7B%0A%09%09va
@@ -725,16 +725,47 @@
  = form;
+%0A%09%09%09this.callbacks = callbacks;
 %0A%0A%09%09%09thi
@@ -1335,24 +1335,290 @@
 response) %7B%0A
+%09%09%09%09%09var decodeResponse = JSON.decode(response);%0A%09%09%09%09%09if (typeof _this.callbacks != 'undefined') %7B%0A%09%09%09%09%09%09var name = _this.form.getAttribute('name');%0A%09%09%09%09%09%09if (typeof _this.callbacks%5Bname%5D == 'function') %7B%0A%09%09%09%09%09%09%09_this.callbacks%5Bname%5D(decodeResponse);%0A%09%09%09%09%09%09%7D%0A%09%09%09%09%09%7D%0A
 %09%09%09%09%09return 
@@ -1641,37 +1641,31 @@
 Success(
-JSON.
 decode
-(r
+R
 esponse)
 );%0A%09%09%09%09%7D
@@ -1656,17 +1656,16 @@
 esponse)
-)
 ;%0A%09%09%09%09%7D,
@@ -1831,24 +1831,24 @@
 = 0; i--) %7B%0A
-
 %09%09%09%09new cowb
@@ -1875,16 +1875,27 @@
  options
+, callbacks
 );%0A%09%09%09%7D%0A

Allow specific callbacks from an external object