/// <summary>
/// AjaxSaveForm submits elements of a form via an XMLHTTPRequest call based on the
/// action and method attributes of the form tag.
/// Element values are submitted URIencoded using the javascript encodeURIComponent function.
/// Checkbox form elements are given values of either "true" or "false" rather than "on" and "null".
/// </summary>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="successFunction">function reference passed to Ajax.Request onSuccess callback</param>
/// <param name="failureFunction">function reference passed to Ajax.Request onFailure callback</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.AjaxSaveForm = function(form, successFunction, failureFunction){
		form=$(form);
		var params = new Array();
		var radios = {};
		form.getElements().each(function(e){
			if( name=e.name.gsub(/.*\$/,'') ){
				if("radio" == e.type){
					if( typeof(radios[name]) == "undefined" ) {
						radios[name] = "null";
					}
					if(e.checked) radios[name]=e.value;
				} else {
					params.unshift( encodeURIComponent(name)+"="+encodeURIComponent(e.trueValue()) );
				}
			}
		});
		$H(radios).each(function(pair){
			params.unshift( encodeURIComponent(pair.key)+"="+pair.value );
		});
		new Ajax.Request(form.action,
			{
				method: form.method,
				parameters: params.join('&'),
				onSuccess: successFunction,
				onFailure: failureFunction,
				evalJSON: 'force'
			});
		return form;
	};
/// <summary>
/// processSaveResults interprets a JSON server response to an Ajax form submission
/// and properly reconfigures the form accordingly.
/// JSON input is expected either as the third parameter or as the second parameter 
/// accessible from response.json. See Prototype's Ajax.Response for more information.
/// 
/// The expected JSON format should follow:
///  {
///    "EditView" : { [{ "ClassName" : string, "DoShow" : bool, "Content" : HTMLstring } , ...] },
///    "StaticView" : { [{ "ClassName" : string, "DoShow" : bool, "Content" : HTMLstring } , ...] },
///    "Message" : { [{ "ClassName" : string, "DoShow" : bool, "Content" : HTMLstring } , ...] },
///    "Error" : { [{ "ClassName" : string, "DoShow" : bool, "Content" : HTMLstring } , ...] },
///    "PreScript" : null | function(){..},
///    "PostScript" : null | function(){...}
///  }
/// </summary>
/// <example>
///  new Ajax.Request( '...', { .. onSuccess: Element.processSaveResults.curry(form.identify()) .. } );
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="response">Ajax.Response object as passed to callback functions defined in Ajax.Request</param>
/// <param name="json">(optional) JSON object as passed to callback functions defined in Ajax.Request</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.processSaveResults = function(form,response,json){
		form=$(form);
		if ( (json!=null) || (json=response.responseJSON) ){
			if(json.PreScript) eval(json.PreScript);
			
			json.Error.each(function(err){
				(err.DoShow)?form.showError(err.ClassName):form.hideError(err.ClassName);
				if(err.Content){form.writeError(err.ClassName, err.Content)};
			});
			json.Message.each(function(msg){
				(msg.DoShow)?form.showMessage(msg.ClassName):form.hideMessage(msg.ClassName);
				if(msg.Content){form.writeMessage(msg.ClassName, msg.Content)};
			});
			$A(json.Static).each(function(static){
				(static.DoShow)?form.showStaticView(static.ClassName):form.hideStaticView(static.ClassName);
				if(static.Content){form.writeStaticView(static.ClassName, static.Content)};
			});
			json.Edit.each(function(static){
				(static.DoShow)?form.showEditView(static.ClassName):form.hideEditView(static.ClassName);
				if(static.Content){form.writeEditView(static.ClassName, static.Content)};
			});

			if(json.PostScript) eval(json.PostScript);
		}
		return form;
	};
///	<summary>
/// writeFailure writes a user-friendly message to a form inside a div with class 'AJAXfailure'.
/// It is intended to use as the onFailure callback to Prototype's Ajax.Request object.
/// </summary>
/// <example>
///   $(form).writeFailure()
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="response">(optional)Ajax.Response object as passed to callback functions defined in Ajax.Request</param>
/// <returns>An object reference to the submitted form</returns>
	Form.Methods.writeFailure = function(form, response) {
	    form = $(form);
	    var msg = "There was an error while submitting the form. Please try again in a few minutes.";

	    if (response != null)
	        msg += "<br/>" + response.status + ": " + response.statusText;
	    else
	        msg += "<br/>response is null.";

	    var failure = (form.select('.AJAXfailure')[0] || form.insert({ top: new Element('div', { 'class': 'AJAXfailure' }) }).select('.AJAXfailure')[0]);
	    $(failure).update(msg);
	    form.showFailure();
	    return form;
	};
/// <summary>
/// save is a wrapper method that prepares callbacks for Ajax.Response so that further post-submit actions
/// defined by the server will be executed on the submitted form.
/// </summary>
/// <example>
///   $(form).save()
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.save = function(form){
		form=$(form);
		form.hideError("").hideFailure("");
		if(!form.validator || form.validator.validate() ) form.AjaxSaveForm(Form.processSaveResults.curry(form.identify()),Form.writeFailure.curry(form.identify()));
		return form;
	};
/// <summary>
/// writeError writes the given content to a div or divs of class 'error' inside the passed-in form object.
/// If the scope parameter is non-empty, the target divs must match .error.<scope>.
/// If a div of that class is not found, one is created at the top of the form, and then updated.
/// </summary>
/// <example>
///   $(form).writeError("","<strong>There was an Error</strong>"); /* Writes message to all .error elements */
///   $(form).writeError("address","<strong>There was an Error</strong>"); /* Writes message to all .error.address elements */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <param name="content">HTML markup to show</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.writeError = function(form,scope,content){
		form=$(form);
		var error = form.select('.error'+ ( (scope)?".":"") + scope )
		if ( 0 == error.length) error=form.insert({top:new Element('div',{'class':'.error'+ ( (scope)?".":"") + scope })}).select('.error'+ ( (scope)?".":"") + scope );
		error.invoke('update',content);
		return form;
	};
/// <summary>
/// writeMessage writes the given content to a div or divs of class 'message' inside the passed-in form object.
/// If a div of that class is not found, one is created at the top of the form, and then updated.
/// </summary>
/// <example>
///   $(form).writeMessage("","User Name updated!"); /* Writes message to all .message elements */
///   $(form).writeMessage("address","User Name updated!"); /* Writes message to all .message.address elements */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <param name="content">HTML markup to show</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.writeMessage = function(form,scope,content){		
		form=$(form);
		var msg = form.select('.message'+ ( (scope)?".":"") + scope )
		if ( 0 == msg.length) msg=form.insert({top:new Element('div',{'class':'message'+ ( (scope)?".":"") + scope})}).select('.message'+ ( (scope)?".":"") + scope );
		msg.invoke('update',content);
		return form;
	};
///<summary>
/// writeStaticView writes the given content to a div or divs of class 'staticView' inside the passed-in form object.
/// If a div of that class is not found, one is created at the top of the form, and then updated.
/// </summary>
/// <example>
///   $(form).writeStaticView("","<strong>User Name:</strong> <em>john.doe</em>"); /* Writes to all .staticView elements */
///   $(form).writeStaticView("address","<strong>User Name:</strong> <em>john.doe</em>"); /* Writes to all .staticView.address elements */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <param name="content">HTML markup to show</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.writeStaticView = function(form,scope,content){
		form=$(form);
		var view = form.select('.staticView'+ ( (scope)?".":"") + scope )
		if ( 0 == view.length) view=form.insert({top:new Element('div',{'class':'staticView'+ ( (scope)?".":"") + scope })}).select('.staticView'+ ( (scope)?".":"") + scope );
		view.invoke('update',content);
		return form;
	};
/// <summary>
/// writeEditView writes the given content to a div or divs of class 'editView' inside the passed-in form object.
/// If a div of that class is not found, one is created at the top of the form, and then updated.
/// </summary>
/// <example>
///   $(form).writeEditView("","<label>User Name:</label> <input value='john.doe' type='text' />"); /* Writes to all .staticView elements */
///   $(form).writeEditView("address","<label>User Name:</label> <input value='john.doe' type='text' />"); /* Writes to all .staticView.address elements */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <param name="content">HTML markup to show</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.writeEditView = function(form,scope,content){
		form=$(form);
		var view = form.select('.editView'+ ( (scope)?".":"") + scope )
		if ( 0 == view.length) view=form.insert({top:new Element('div',{'class':'editView'+ ( (scope)?".":"") + scope })}).select('.editView'+ ( (scope)?".":"") + scope );
		view.invoke('update',content);
		return form;
	};
/// <summary>
/// showFailure shows the div of class 'AJAXfailure' found in the form reference passed in.
/// A div of class 'AJAXfailure' is assumed to exist.
/// </summary>
/// <example>
///   $(form).showFailure();
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.showFailure = function(form){
		form=$(form);
		form.select('.AJAXfailure')[0].style.display="block";
		form.addClassName('failureView');
		return form;
	};
/// <summary>
/// showError shows the div or divs of class 'error' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).showError(""); /* shows elements of class .error */
///   $(form).showError("address"); /* shows elements of class .error.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.showError = function(form,scope){
		form=$(form);
		form.select('.error'+ ( (scope)?".":"") + scope ).invoke('show');
		form.addClassName('errorView');
		return form;
	};
/// <summary>
/// showMessage shows the div or divs of class 'message' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).showMessage(""); /* shows elements of class .message */
///   $(form).showMessage("address"); /* shows elements of class .message.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.showMessage = function(form,scope){
		form=$(form);
		form.select('.message'+ ( (scope)?".":"") + scope ).invoke('show');
		form.addClassName('messageView');
		return form;
	};
/// <summary>
/// showStaticView shows the div or divs of class 'staticView' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).showStaticView(""); /* shows elements of class .staticView */
///   $(form).showStaticView("address"); /* shows elements of class .staticView.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.showStaticView = function(form,scope){
		form=$(form);
		form.select('.staticView'+ ( (scope)?".":"") + scope ).invoke('show');
		form.removeClassName('editView').addClassName('staticView');
		return form;
	};
/// <summary>
/// showEditView shows the div or divs of class 'editView' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).showEditView(""); /* shows elements of class .editView */
///   $(form).showEditView("address"); /* shows elements of class .editView.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.showEditView = function(form,scope){
		form=$(form);
		form.select('.editView'+ ( (scope)?".":"") + scope ).invoke('show');
		form.removeClassName('staticView').addClassName('editView');
		return form;
	};
/// <summary>
/// hideFailure hides the div of class 'AJAXfailure' found in the form reference passed in.
/// If a div of that class is not found, one is created at the top of the form, and then hidden.
/// </summary>
/// <example>
///   $(form).hideFailure();
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.hideFailure = function(form){
		form=$(form);
		var failure = ( form.select('.AJAXfailure')[0] || form.insert({ top:new Element('div',{'class':'AJAXfailure'}) }).select('.AJAXfailure')[0] );
		failure.style.display="none";
		form.removeClassName('failureView');
		return form;
	};
/// <summary>
/// hideError hides the div or divs of class 'error' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).hideError(""); /* shows elements of class .error */
///   $(form).hideError("address"); /* shows elements of class .error.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.hideError = function(form,scope){
		form=$(form);
		form.select('.error'+ ( (scope)?".":"") + scope ).invoke('hide');
		form.removeClassName('errorView');
		return form;
	};
/// <summary>
/// hideMessage hides the div or divs of class 'message' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).hideMessage(""); /* shows elements of class .message */
///   $(form).hideMessage("address"); /* shows elements of class .message.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.hideMessage = function(form,scope){
		form=$(form);
		form.select('.message'+ ( (scope)?".":"") + scope ).invoke('hide');
		form.removeClassName('messageView');
		return form;
	};
/// <summary>
/// hideStaticView hides the div or divs of class 'staticView' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).hideStaticView(""); /* shows elements of class .staticView */
///   $(form).hideStaticView("address"); /* shows elements of class .staticView.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.hideStaticView = function(form,scope){
		form=$(form);
		form.select('.staticView'+ ( (scope)?".":"") + scope ).invoke('hide');
		form.removeClassName('staticView');
		return form;
	};
/// <summary>
/// hideEditView hides the div or divs of class 'editView' found in the form reference passed in.
/// </summary>
/// <example>
///   $(form).hideEditView(""); /* shows elements of class .editView */
///   $(form).hideEditView("address"); /* shows elements of class .editView.address */
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <param name="scope">a string with the class to filter the targets by</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.hideEditView = function(form,scope){
		form=$(form);
		form.select('.editView'+ ( (scope)?".":"") + scope ).invoke('hide');
		form.removeClassName('editView');
		return form;
	};
/// <summary>
/// edit switches the form view to hide Failure,Error and StaticView states and show the editView state.
/// It also iterates through the form elements and saves their values to the tag's init attribute.
/// </summary>
/// <example>
///   $(form).edit();
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.edit = function(form){
		form=$(form);
		form.hideStaticView().hideFailure().hideError().showEditView();
		form.select('.editView')[0].select('input[type~=text]','input[type~=checkbox]','input[type~=radio]','select','textarea').each(function(e){
			e.writeAttribute('init',e.trueValue());
		});
	};
/// <summary>
/// cancel switches the form view to hide Failure,Error and editView states and show the staticView state.
/// It also iterates through the form elements refreshes their values from the tag's init attribute.
/// </summary>
/// <example>
///   $(form).cancel();
/// </example>
/// <param name="form">either id or object reference to the form being submitted</param>
/// <returns>An object reference to the submitted form</returns>
Form.Methods.cancel = function(form){
		form=$(form);
		form.hideEditView().hideFailure().hideError().showStaticView();
		(form.validator && form.validator.reset())
		form.select('.editView')[0].select('input[type~=text]','input[type~=checkbox]','input[type~=radio]','select','textarea').each(function(e){
			e.trueValue(e.readAttribute('init'));
		});
	};
/// <summary>
/// trueValue gets/sets a form element's value taking into consideration the type of input it is.
/// Checkboxes and Radio buttons are accessed through their native "checked" attribute and return a bool as a string,
/// whereas other inputs are accessed through their "value" attribute.
/// Multiple selects are handled specially, the selected values are returned/set using a comma separated list of values.
/// </summary>
/// <example>
///  $('checkbox_id').trueValue(true);
///  $('checkbox_id').trueValue(); /* true */
/// </example>
/// <param name="e">form element to operate on</param>
/// <param name="val">(optional) value to set element to</param>
/// <returns>current value of form element</returns>
Form.Element.Methods.trueValue = function(e,val){
		var tag = e.getTag();
		var type = e.type;
		if("input" == tag && (("radio" == type) || ("checkbox" == type)) ) {
				if(typeof(val)!="undefined"){
					(val=="true")?e.checked=true:e.checked=false;
				}
				return e.checked+"";
		} else if ( ("select" == tag) && ("multiple" == e.readAttribute('multiple')) ){
				if(typeof(val)!="undefined"){
					var valArr = val.split(',');
					e.selectedIndex = -1;
					$A(e.options).findAll(function(o){
						return valArr.include(o.value);
					}).each(function(o){o.selected=true});
				}
				return $A(e.options).findAll(function(o){return o.selected}).pluck('value').join(',');
		} else {
				if (typeof(val)!="undefined") e.value=val;
				return e.value;
		}
	};
Element.addMethods();