
/*
 * jQuery Deliver Plugin
 * @author Ramon Lamana
 * @link http://www.rlamana.es
 * 
 * @version 0.12 (9.Jun.2009)
 * @requires jQuery v1.3.1 or later
 * 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function(jQuery) 
{
	/**
	 * Package constructor and selector. Works like jQuery init function.
	 * 
	 * It behaves like an HTML form. You can define an action
	 * and the method used to send the package.
	 * 
	 * @param {string} name Package name (package to select or to create)
	 * @param {string} [action] URL where the package will be sent
	 * @param {string} [method] 'POST' or 'GET' method selection (default: 'POST')
	 * @return {mixed} boolean when creating a package or the package when selecting one
	 * @addon
	 */
	jQuery.package = function(name)
	{
		if (arguments.length == 0) return;
		
		var name = arguments[0];
		
		// Serveral arguments -> Package creator
		if (arguments.length > 1)
		{
			// Create package empty object
			jQuery.package[name] = {};			
			
			// Load properties
			jQuery.package[name].name = name;
			jQuery.package[name].action = arguments[1];
			jQuery.package[name].method = (arguments[2] != undefined) ? arguments[2] : "POST";
			jQuery.package[name].fields = {};
			
			// Set a default deliver event
			jQuery.package(name).deliver(function(){return true;});
						
			return true;
			
		// One argument -> Package selector
		} 
		else 
		{
			// We inject the functions to the package element
			if (jQuery.package[name])
				return jQuery.extend(jQuery.package[name], jQuery.package.fn);
		}
	};
	
	/**
	 * Package's functions associative array
	 * @member package
	 */
	jQuery.package.fn =
	{
		/**
		 * Sets or Gets a field value
		 */
		field : function ()
		{
			if (arguments.length == 0) return;
			return (arguments.length == 1) ? 
					this.fields[arguments[0]] : 
					this.fields[arguments[0]] = arguments[1];
		},

		
		/**
		 * Serializes all field elements in the package 
		 * into a query string.
		 * 
		 * @return {string} A string in the format: field1=value1&amp;field2=value2
		 */
		serialize: function ()
		{
			 var pkgdata = "";
			 // Arreglado 'bug' que hacia que petara cuando no era un String
			 jQuery.each(this.fields, function(name,value){pkgdata += name + "=" + jQuery.trim(value.toString()) + "&";});   
			 return pkgdata;
		},
		
		/**
		 * Packs content into the package
		 * @param {object} Associative array {fieldname : fieldvalue, ...} 
		 */
		pack : function (content)
		{
			jQuery.extend(this.fields, content);
		},
		

		/**
		 * Binds a function to the SUCCESS event of 
		 * a package or trigger event.
		 * 
		 * The SUCCESS handler will be called if
		 * package was sent without errors.
		 *
		 * Handler should look like this:
		 * 		function (event, response) {}
		 *		 
		 * @param {function} fn The callback function
		 */
		success : function (fn)
		{
			// Check type of obj (html form or package object)
			var obj = this.fields ? this : this[0];	 
			
			if(!obj) return;
			
			// Trigger or create SUCCESS event
			fn ?	jQuery.event.add(obj, "success", fn, null) :  
					_trigger(obj, "success", null);
		},
		

		/**
		 * Binds a function to the DELIVER event of 
		 * a package or trigger event if no parameters.
		 * 
		 * If the callback function returns FALSE, package
		 * won't be sent.
		 * 
		 * The DELIVER handler will be called before
		 * package is sent.
		 * 
		 * @param {mixed} fn The callback function or an associative 
		 *		array with additional package content to be sent.
		 */
		deliver : function (fn)
		{		
			// Check type of obj (html form or package object)
			var obj = this.fields ? this : this[0]; 

			// Callback passed -> Add DELIVER event to package or form
			if(typeof fn == "function")
			{
				// Comprobamos que se haya seleccionador algun objetos 
				if(!obj) return;
				
				jQuery.event.add(obj, "deliver", fn, null);
				
				// If it is a FORM, then we set SUBMIT to trigger DELIVER event too
				// Submit will return false to avoid the form to be posted
				if (!this.fields) jQuery(this).submit(function(){jQuery(this).deliver();return false;});
			}	
			else
			{		
				// We set package content
				if ((typeof fn == "object") && this.fields) obj.pack(fn);
				
				// Different serialize for form or package objects			
				var pkg_data = this.fields ? obj.serialize() : $(obj).serialize(); 

				if(!_trigger(obj, "deliver", null))
					return false;
				
				// Send form
				$.ajax
				({
					type: obj.method,
					url: obj.action,
					data: pkg_data,
					success: function(response){_trigger(obj, "success", [response]);},
					error: function(http_request, text_status, error_thrown){_trigger(obj, "fail", [text_status]);}
				});
				
				return true;
			}

		},
		
		
		/**
		 * Binds a function to the ERROR event of 
		 * a package or trigger event.
		 * 
		 * The DELIVER handler will be called if there were errors
		 * delivering the package. 
		 * 
		 * Handler should look like this:
		 * 		function (event, response) {}
		 * @param {function} fn The callback function
		 */
		fail : function (fn)
		{
			// Check type of obj (html form or package object)
			var obj = this.fields ? this : this[0];	 
			
			if(!obj) return;
			
			// Trigger or create ERROR event
			fn ?	jQuery.event.add(obj, "fail", fn, null) :  
					_trigger(obj, "fail", null);
		}
	}; // End package associative array
	
	
	/**
	 * jQuery extension to handle forms like packages
	 */	
	jQuery.fn.extend
	({			
		/**
		 * Sets or Gets a field value in a Form
		 * @param {string} Field name
		 * @param {string} Optional fields value to set
		 * @return {mixed} Field value if get operation or bool value
		 * 			if set operation
		 */
		field : function ()
		{		
			if (arguments.length == 0) return;
			
			// Take form html element
			var form = this[0];
			var fieldname = arguments[0];
			
			var formfield = undefined;
			jQuery(':input', form).each(function()
			{
				if(this.name == fieldname)
				{
					formfield = this;
				    return;
				}
			});
			
			if(arguments.length == 1)
				return formfield.value;
			else
				formfield.value = arguments[1];
		},
		
		success: jQuery.package.fn.success,
		deliver: jQuery.package.fn.deliver,
		fail: jQuery.package.fn.fail,
		
		/**
		 * Packs a HTML Form in order to later send it via Ajax
		 * and activate DELIVER, SUCCESS and FAIL events.
		 */
		pack: function()
		{
			// Set a simple deliver callback that always validates
			// the form
			jQuery(this).deliver(function(){return true;});
		}
		 

		
	}); // End jQuery Extension
		
	
	/**
	 * Triggers package events without propagation
	 * and returns the return value of the 
	 * triggered handler.
	 * @param {object} elem Element on which the event is triggered
	 * @param {string} type Event type
	 * @param {array} data Data array for the event handler
	 * @private
	 */
	function _trigger (object, type, data) 
	{
		if(object)
		{
			var event = jQuery.Event(type);
			event.preventDefault();
			event.stopPropagation();
			jQuery.event.trigger(event, data, object);
			return event.result;
		}		
	}	
	
})(jQuery);
// Plugin closure

